本文最后更新于2019年05月08日,有任何建议或疑问,欢迎底部讨论。

XSS,Cross Site Scripting,跨站脚本攻击。
DVWA中 XSS (Reflected) 和 XSS (Stored) 代码等级类似,只是表现形式不一样,反射型和存储型,故合二为一。

XSS类型

  • 反射型:发生在客户端,非持续型。输入--输出。
  • 存储型:发生在服务端,持续型。输入--写入数据库-读取数据库--输出。
  • DOM型:Document Object Model,文档对象模型。发生在客户端,简单可理解为 javascript 代码中的xss漏洞。输入--输出。

一、反射型XSS

Low

<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Feedback for end user
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>

代码未做过滤,输入直接 echo 输出。
<script>alert(/xss/)</script> <img src=1 onerror=alert(/xss/)>

Medium

<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = str_replace( '<script>', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
?>

中级代码使用 str_replace() 函数过滤了 <script>
双写 <script> 绕过: <scr<script>ipt>alert(/xss/)</script>
字母大小写绕过: <ScRipt>alert(/xss/)</script>
使用不带 <script> 的代码(见High)

High

<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}
?>

高等级代码使用 preg_replace() 正则,将 <script> 每个字符都进行检查,加了 i 参数,不区分大小写,置换为空。

使用不带 <script> 参数的xss语句绕过:
<img src=1 onerror=alert(/xss/)>
<iframe onload="alert(1)"></iframe>
<body onload=alert('XSS')>
<input type="text" value="helloword" onclick="alert(1)"/>

Impossible

<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $name = htmlspecialchars( $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

// Generate Anti-CSRF token
generateSessionToken();
?>

该段代码加入了 Anti-CSRF token,且输入参数都经 htmlspecialchars() 实体化了,转义后执行不了js语句从而防止跨站。

二、存储型XSS

Low

<?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();
}
?>
  • trim() 函数,移除字符串两侧的空白字符或预设字符(\0\t\n\x0B\r空格
  • stripslashes() 函数,删除字符串中的反斜杠
  • mysql_real_escape_string() 函数,对 \x00\n\r\'"\x1a 字符进行转义

虽然用了这些函数做了处理,但不影响xss语句。
直接在message里输入 <script>alert(/xss/)</script> 执行存储型xss。

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

中级代码使用的函数:

  • trim() 函数,移除字符串两侧的空白字符或预设字符(\0\t\n\x0B\r空格
  • addslashes() 函数,在特定字符('"\null)前面加反斜杠 \
  • strip_tags() 函数,剥去字符串中的 HTML 标签
  • mysql_real_escape_string() 函数,对 \x00\n\r\'"\x1a 字符进行转义
  • htmlspecialchars(),html实体化

此处大多都是对 message 参数做的过滤,所以该字段无法执行xss。但 name 参数只过滤了 <script> ,可导致xss。
注意:name 在页面限制了字符长度,因此得抓包修改参数。(可双写、大小写、使用不带 <script> 语句绕过)
2019-05-08T12:26:20.png

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 = mysql_real_escape_string( $message );
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $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();
}
?>

对 message 做了严格的检查、过滤,对 name 做了 <script> 做了正则大小写替换,因此,抓包在 name 处使用不带 <script>的xss语句可触发。

<img src=1 onerror=alert(/xss/)>
<iframe onload="alert(1)"></iframe>
<body onload=alert('XSS')>
<input type="text" value="helloword" onclick="alert(1)"/>

2019-05-08T12:26:33.png

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

对 name、message 参数都限制严格,故不存在xss。

文章目录