DVWA学习(五)XSS

引言

DVWA学习(五)XSS

简介

XSS,全称Cross Site Scripting,即跨站脚本攻击,某种意义上也是一种注入攻击,是指攻击者在页面中注入恶意的脚本代码,当受害者访问该页面时,恶意代码会在其浏览器上执行,需要强调的是,XSS不仅仅限于JavaScript,还包括flash等其它脚本语言。

根据恶意代码是否存储在服务器中,XSS可以分为存储型的XSS与反射型的XSS。

DOM型的XSS由于其特殊性,常常被分为第三种,这是一种基于DOM树的XSS。例如服务器端经常使document.boby.innerHtml等函数动态生成html页面,如果这些函数在引用某些变量时没有进行过滤或检查,就会产生DOM型的XSS。DOM型XSS可能是存储型,也有可能是反射型。

Reflected Cross Site Scripting (反射型XSS)

LOW级别

输入的数据会显示在当前页面中,尝试输name=aaaa<>'/"发现没有任何过滤

1
name=<script>alert(1)</script>

1
name=<img src=1 onerror=alert(1)>

源码

1
2
3
4
5
6
7
8
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>

array_key_exists检查数组中是否有指定的键名
传入的name参数没有任何过滤就输出到页面上。

Medium难度

1
name=aaaa<>'/",发现没有过滤<>'/"。

尝试插入JS语句name=<script>alert(1)</script>没有弹窗查看源码,<script> 被过滤了。

尝试用大小写绕过

**<script/>**也可以绕过

双写<script>绕过

过滤了script可以用img标签

body标签

iframe标签

input标签

还有很多。

源码

1
2
3
4
5
6
7
8
9
10
11
<?php
header ("X-XSS-Protection: 0");
// 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>";
}
?>

只过滤了<script>

High难度

同样没有过滤aaaa<>'/" name=<script>alert(1)</script>就剩下了个>

用img标签

同样可以用iframe和body等标签。

源码

1
2
3
4
5
6
7
8
9
10
11
<?php
header ("X-XSS-Protection: 0");
// 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>";
}
?>

源码把<script>替换为空.

impossible级别

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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();
?>

使用htmlspecialchars函数把预定义的字符&、”、 ’、<、>转换为 HTML 实体,防止浏览器将其作为HTML元素。

存储型XSS

LOW级别

输入测试数据 aaa<>"/'

查看源代码发现没有任何过滤

但是name限制输入字符为10个,可以用hackbar改包

1
txtName=<script>alert(/xss/)</script>&mtxMessage=<script>alert(/xss/)</script>

查看源代码

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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();
}
?>

trim函数去掉字符序列左边和右边的空格。
stripslashes函数删除message中的反斜杠。
mysqli_real_escape_string函数转义namemessage中的\x00 ,\n,\r,\ , '," , \x1a
没有做XSS方面的过滤与检查,并且存储在数据库中,所以存在出存储型XSS。

Medium难度

同样输入txtName=<script>alert(/xss/)</script>&mtxMessage=<script>alert(/\xss/)</script>前者被过滤了<script>,后者script标签都被过滤

尝试大小写script绕过,name成功弹窗,message仍然过滤

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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();
}
?>

strip_tags函数剥去字符串中的 HTML 标签。但是仍然可以使用标签
addslashes() 函数返回在预定义字符(单引号、双引号、反斜杠、NULL)之前添加反斜杠的字符串。
message参数使用了htmlspecialchars函数进行HTML编码,无法注入XSS代码。

High难度

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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();
}
?>

name过滤了<script,使用其他标签如iframe即可。

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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();
?>

采用了token机制防止CSRF攻击,对name和message都采用htmlspecialchars函数编码防止XSS,sql语句用pdo对象防止SQL注入。

DOM型XSS

LOW级别

是个下拉框,不过可以抓包改参数

查看源代码

indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
substring() 方法用于提取字符串中介于两个指定下标之间的字符。

1
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");

default=后面的参数变成了该语句中的lang,也就是:
<option value="lang">decodeURI(lang)</option>

同样可以闭合前面的option标签和select标签

medium级别

PHP源码

1
2
3
4
5
6
7
8
9
10
11
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];
# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}
?>

stripos,查找 “第二个参数” 在第一个参数中第一次出现的位置。
也就是说过滤了<script

绕过方法1
url中有一个字符为#,该字符后的数据不会发送到服务器端,从而绕过服务端过滤,构造连接为

1
default=<script>alert(/xss/)</script>

绕过方法2

用img,iframe,body,svg等标签的特性去执行JS代码。

high级别

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {

# White list the allowable languages
switch ($_GET['default']) {
case "French":
case "English":
case "German":
case "Spanish":
# ok
break;
default:
header ("location: ?default=English");
exit;
}
}
?>

限定了键名必须为default,键值必须为固定的4个值。

所以这里只能用#绕过服务器检测

impossible级别

把decodeuri变成了一个简单的括号,js是从URL中获得的lang参数并不会对他进行解码。所以无法构造XSS代码。

介绍:仅供技术交流学习探讨,请勿用于非法用途。本文部分资源来源于网络,如有侵权请联系版主删除。

-------------本文结束感谢您的阅读-------------
纯属好玩,打赏多少您随意!