Loading... # 1、目标 基于白盒测试方法,完成存储型 XSS 利用。 ![图片.png](http://47.117.131.13/usr/uploads/2022/03/681993278.png) # 2、级别 Low 关键代码,插入数据库: ```php <?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(); } ?> ``` 输出表格内容到页面: ```php // XSS Stored guestbook function -- function dvwaGuestbook() { $query = "SELECT name, comment FROM guestbook"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); $guestbook = ''; while( $row = mysqli_fetch_row( $result ) ) { if( dvwaSecurityLevelGet() == 'impossible' ) { $name = htmlspecialchars( $row[0] ); $comment = htmlspecialchars( $row[1] ); } else { $name = $row[0]; $comment = $row[1]; } $guestbook .= "<div id=\"guestbook_comments\">Name: {$name}<br />" . "Message: {$comment}<br /></div>\n"; } return $guestbook; } // -- END (XSS Stored guestbook) ``` 前端代码: ```html <table width="550" border="0" cellpadding="2" cellspacing="1"> <tr> <td width="100">Name *</td> <td><input name="txtName" type="text" size="30" maxlength="10"></td> </tr> <tr> <td width="100">Message *</td> <td><textarea name="mtxMessage" cols="50" rows="3" maxlength="50"></textarea></td> </tr> <tr> <td width="100"> </td> <td> <input name="btnSign" type="submit" value="Sign Guestbook" onclick="return validateGuestbookForm(this.form);" /> <input name="btnClear" type="submit" value="Clear Guestbook" onClick="return confirmClearGuestbook();" /> </td> </tr> </table> ``` 关键信息: * 插入数据库前通过 mysqli_real_escape_string 过滤参数 * 将 name 及 comment 输出到页面时没做任何过滤 * 前端限制 Name 最长 10 个字符,Message 最长 50 个字符 由此页面存在存储型的 xss 漏洞,输入: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/1927651252.png) 结果: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/461769671.png) ![图片.png](http://47.117.131.13/usr/uploads/2022/03/3151807498.png) 前端的限制通过抓包修改可以绕过: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/1026314491.png) 结果: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/2834829948.png) 查看数据库: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/406958388.png) # 3、级别 Medium 关键代码: ```php <?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(); } ``` 相较于级别 Low 改动为: * message 参数增加 strip_tags 及 htmlspecialchars 过滤 * name 参数增加 str_replace 过滤 strip_tags 用于从一个字符串中抽除 HTML 和 PHP 的标签,htmlspecialchars 将部分字符转换为 HTML 实体。增加这两项过滤逻辑后,message 很难被利用了。 但是 name 参数只替换了 `<script>` 串,而且这个替换逻辑本身就有缺陷。name 可以继续利用的点有: * 大小写绕过`<script>` * 重新组合`<script>` * 换用`<img>`、`<a> 、<body> 等标签的 src 或 onload、onerror 等事件处理器` 还是改包绕过前端限制: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/1946973737.png) 结果: ![图片.png](http://47.117.131.13/usr/uploads/2022/03/958453307.png) ![图片.png](http://47.117.131.13/usr/uploads/2022/03/3798459444.png) # 4、级别 High 关键代码: ```php <?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(); } ?> ``` 相较于级别 Medium 改动为: * 对 name 参数的过滤更改为 preg_replace 使用正则 preg_replace 过滤,`<script>` 基本上不可用了,但是 `<a>`、`<img>` 等标签依然没有过滤,这里不详述了。 # 5、级别 Impossible 关键代码: ```php <?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(); ?> ``` 相较于级别 High 改动为: * 使用 trim 过滤 message 及 name 参数 * 使用 htmlspecialchars 过滤 message 及 name 参数 * 使用 PDO 预处理方法查询数据库 * 增加 Anti-CSRF token 输出逻辑也有改动,数据先经 htmlspecialchars 过滤再输出: ```php // XSS Stored guestbook function -- function dvwaGuestbook() { $query = "SELECT name, comment FROM guestbook"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ); $guestbook = ''; while( $row = mysqli_fetch_row( $result ) ) { if( dvwaSecurityLevelGet() == 'impossible' ) { $name = htmlspecialchars( $row[0] ); $comment = htmlspecialchars( $row[1] ); } else { $name = $row[0]; $comment = $row[1]; } $guestbook .= "<div id=\"guestbook_comments\">Name: {$name}<br />" . "Message: {$comment}<br /></div>\n"; } return $guestbook; } // -- END (XSS Stored guestbook) ``` # 6、总结 从 Low 级别到 Impossible 级别,防御 XSS 攻击的思路总结为: * 使用 strip_tags、str_replace、preg_replace、mysqli_real_escape_string、htmlspecialchars 等方法对参数进行过滤 * 前端限制参数长度 * 使用 PDO 操作数据库 * 使用 Anti-CSRF token 最后修改:2022 年 03 月 23 日 11 : 39 PM © 允许规范转载