0x00 XSS (Stored)
反射型的XSS一般会直接echo输出到html文件里,而存储型XSS则是先保存到数据库,然后再在某个页面查询显示出来。
0x01 Low
源码分析
<?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();
}
?>
这里只使用mysqli_real_escape_string函数对输入进行SQL注入的过滤,但没有过滤XSS,直接insert到数据库中。然后我们尝试随便输入name和message提交,会在当前页面输出,所以可以进行存储型XSS利用。
解题思路
直接向name或message输入框中输入以下代码,提交即可构成存储型XSS。
<script>alert(1);</script>
注意name输入框有前端字数限制,F12打开开发者工具进行修改即可。
注入XSS之后即使点击了其他页面,再点回来,还是会有XSS弹窗,这就是反射型XSS和存储型XSS的区别。所以实验完成后需要删除该数据,以便后续实验。
0x02 Medium
源码分析
<?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();
}
?>
源码中对message变量做了几个操作:
- addslashes()函数对’、”、\、NULL等前面添加反斜杠进行转义,以防SQL注入。
- strip_tags(string,allow)函数剥离string中的HTML标签,但允许allow字符串中的标签存在。
- mysqli_real_escape_string()函数转义SQL语句中使用的特殊符号,防止SQL注入。
- htmlspecialchars()函数将HTML特殊符号,如<,转为HTML实体。
对name变量只做了str_replace()替换script标签,然后mysqli_real_escape_string()函数防SQL注入,那么就可以利用双写或大小写的方式绕过替换函数。
解题思路
修改前端name输入框的maxlength为10000,然后向name输入框中输入以下代码,提交即可构成存储型XSS。
<scr<script>ipt>alert(1);</script>
或者
<Script>alert(1);</scrIpt>
提交出现弹窗,成功!当然,实验完成后还是需要删除该数据,以便后续实验。
0x03 High
源码分析
<?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();
}
?>
源码中message变量和Medium级别中的一样无法绕过。
而name变量做了preg_replace正则匹配大小写的script标签,所以已经无法使用script标签进行XSS攻击了,但是可以使用img等其他标签。
解题思路
修改前端name输入框的maxlength为10000,然后向name输入框中输入以下代码,提交即可构成存储型XSS。
<img src='1' onerror='alert(1);'/>
提交出现弹窗,成功!当然,实验完成后还是需要删除该数据,以便后续实验。
0x04 Impossible
源码分析
<?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();
?>
源码中对message和nmae变量都做了几个操作:
- addslashes()函数对’、”、\、NULL等前面添加反斜杠进行转义,以防SQL注入。
- strip_tags(string,allow)函数剥离string中的HTML标签,但允许allow字符串中的标签存在。
- mysqli_real_escape_string()函数转义SQL语句中使用的特殊符号,防止SQL注入。
- htmlspecialchars()函数将HTML特殊符号,如<,转为HTML实体。
- PDO技术对用户输入变量做了预编译,以防SQL注入。
经过上述这些操作基本就无法进行存储型XSS注入和SQL注入了。
解题思路
无。
0x05 小结
防御方法:
- 过滤用户输入,如addslashes转义、strip_tags剥离HTML标签、preg_replace黑名单匹配、htmlspecialchars转HTML实体等过滤用户输入。
- 使用OWASP等安全XSS处理API。
- 同时存储数据前还要进行mysqli_real_escape_string转义、PDO预编译等手段,防止SQL注入。