存儲型XSS:會把用戶輸入的數據「存儲」在服務器端,通常出如今須要用戶能夠輸入數據的地方,好比網站的留言板、評論等地方,當網站這些地方過濾不嚴格的時候,就會被黑客注入惡意攻擊代碼,存儲在服務器端,每當用戶加載該頁面時,都會受到攻擊,因此這種攻擊行爲也稱爲「持久型XSS(Persistent XSS)」。php
在Name和Message輸入框中分別輸入一個String(admin)發現輸入的數據會顯示在當前頁面中,同時能夠知道數據提交是以POST請求的方式html
查看源碼:mysql
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = stripslashes( $message ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Sanitize name input $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>
代碼審計發現,並不存在Protect,trim()函數、stripslashes()函數、mysqli_real_escape_string()
函數等不會對JS代碼進行過濾sql
trim(string,charlist) 函數移除字符串兩側的空白字符或其餘預約義字符。 string 必需。規定要檢查的字符串。 charlist 可選。規定從字符串中刪除哪些字符。若是被省略,則移除如下全部字符: "\0" - NULL "\t" - 製表符 "\n" - 換行 "\x0B" - 垂直製表符 "\r" - 回車 " " - 空格 ######################################################################################## stripslashes(string) 函數刪除由 addslashes() 函數添加的反斜槓。 string 必需。規定要檢查的字符串。 提示:該函數可用於清理從數據庫中或者從 HTML 表單中取回的數據。 ######################################################################################## addslashes(string) 函數返回在預約義字符以前添加反斜槓的字符串。 預約義字符是: 單引號(') 雙引號(") 反斜槓(\) NULL string 必需。規定要轉義的字符串。 提示:該函數可用於爲存儲在數據庫中的字符串以及數據庫查詢語句準備字符串。 ######################################################################################## mysql_real_escape_string(string,connection) 函數轉義 SQL 語句中使用的字符串中的特殊字符。 下列字符受影響: \x00 \n \r \ ' " \x1a 若是成功,則該函數返回被轉義的字符串。若是失敗,則返回 false。 string 必需。規定要轉義的字符串。 connection 可選。規定 MySQL 鏈接。若是未規定,則使用上一個鏈接。 提示:可以使用本函數來預防數據庫攻擊。
構造payload,執行最簡單的XSS攻擊數據庫
POST data
txtName=admin&mtxMessage=<script>alert("_XSS_")</script>&btnSign=Sign+Guestbook
惡意代碼被存入服務端,每當用戶打開該網頁都會受到攻擊數組
服務端數據庫文件已存入攻擊者的惡意代碼服務器
進入該網頁,觸發惡意攻擊session
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = str_replace( '<script>', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>
通過代碼審計,能夠發現medium級別進行了Protect,對POST請求到的的內容進行過濾:app
str_replace()函數 對 Name data進行<script>標籤匹配置空函數
strip_tags()函數 對 message data進行標籤匹配刪除
str_replace(find,replace,string,count) 函數以其餘字符替換字符串中的一些字符(區分大小寫)。 該函數必須遵循下列規則: 若是搜索的字符串是數組,那麼它將返回數組。 若是搜索的字符串是數組,那麼它將對數組中的每一個元素進行查找和替換。 若是同時須要對數組進行查找和替換,而且須要執行替換的元素少於查找到的元素的數量,那麼多餘元素將用空字符串進行替換 若是查找的是數組,而替換的是字符串,那麼替代字符串將對全部查找到的值起做用。 註釋:該函數區分大小寫。請使用 str_ireplace() 函數執行不區分大小寫的搜索。 參數 描述 find 必需。規定要查找的值。 replace 必需。規定替換 find 中的值的值。 string 必需。規定被搜索的字符串。 count 可選。對替換數進行計數的變量。 ########################################################################################### strip_tags(string,allow) 函數剝去字符串中的 HTML、XML 以及 PHP 的標籤。 註釋:該函數始終會剝離 HTML 註釋。這點沒法經過 allow 參數改變。 參數 描述 string 必需。規定要檢查的字符串。 allow 可選。規定容許的標籤。這些標籤不會被刪除。
分析可知要想利用XSS攻擊,只有對Name data進行注入,利用<script>標籤大小寫進行繞過,但你會發現Name data length=10 受到限制
篡改數據,繞過length限制
構造payload
POST Name data
txtName=%3CScript%3Ealert%281111%29%3C%2FscriPt%3E&mtxMessage=admin&btnSign=Sign+Guestbook
Name data的繞過
服務端數據庫文件已存入攻擊者的惡意代碼
在這裏也可使用<img>標籤進行繞過:<img src=# onerror=alert("_XSS_")>
<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>
代碼審計以後,發現high級別的Name data存在另一種的Protect,利用preg_replace()函數進行匹配「< s r i p t」等字符,將其置爲空,此時Name data裏面的<script>標籤是不能使用的,無論是大小進行區分寫都不能夠進行繞過
因此,就須要利用其它標籤進行XSS攻擊,此處能夠利用 medium 級別 中提到的<img>標籤進行繞過preg_replace()函數的Protect
構造payload
POST Name data
txtName=%3Cimg+src%3D%23+onerror%3Dalert%28%22_XSS_%22%29%3E&mtxMessage=Hello+admin&btnSign=Sign+Guestbook
<img>標籤成功繞過preg_replace()
服務端數據庫文件已存入攻擊者的惡意代碼
<?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 = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = stripslashes( $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $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(); ?>
impossible 級別的Protect不能被繞過,因爲Name data和Message data都受到htmlspecialchars()函數的保護做用
htmlspecialchars() 函數把預約義的字符轉換爲 HTML 實體。 預約義的字符是: & (和號)成爲 & " (雙引號)成爲 " ' (單引號)成爲 ' < (小於)成爲 < > (大於)成爲 > 它的語法以下: htmlspecialchars(string,flags,character-set,double_encode) 其中第二個參數flags須要重要注意,不少開發者就是由於沒有注意到這個參數致使使用htmlspecialchars()函數過濾XSS時被繞過。由於flags參數對於引號的編碼以下: 可用的引號類型: ENT_COMPAT - 默認。僅編碼雙引號。 ENT_QUOTES - 編碼雙引號和單引號。 ENT_NOQUOTES - 不編碼任何引號。 默認是隻編碼雙引號的
由於輸入的全部標籤都被轉義,因此此處不存在XSS攻擊,可是要注意flags屬性,使用不當過濾XSS時就會被繞過