漏洞简介
跨站脚本攻击又名XSS,全称为Cross Site Scripting,为了区别层叠样式表(CSS)所以简称XSS。XSS漏洞是指恶意攻击者向Web界面插入恶意的Script代码,当被攻击者访问该界面时触发恶意的Script代码,从而完成恶意攻击者的攻击。
XSS漏洞是Web应用系统中出现频率最多的漏洞之一,图1为OWASP项目提出的”十大Web应用安全风险”,简称为OWASP Top 10。OWASP Top 10对安全问题从威胁和脆弱性进行可能性分析,并结合技术和商业影响的分析,输出目前一致公认、最严重的十类web应用安全风险排名。可以看到图片中分别为2013年版以及2017年版,XSS漏洞分别出现在A3以及A7,虽然较2013版排名有所滑落,但是仍挤在十大Web应用安全风险之列。
(图 1)
漏洞风险
XSS漏洞可导致用户的认证信息被盗,被钓鱼,挂马,还可以组合CSRF漏洞造成更严重的漏洞,如控制业务数据增删改查等等,当然也可以配合其他漏洞进行更深层次的渗透攻击,内网渗透等等。
漏洞分类
首先我们介绍下常见的XSS漏洞的分类,通常分为三大类型,反射型XSS,存储型XSS,以及DOM型XSS。
反射型XSS:非持久化攻击,恶意攻击者在url链接构造好攻击链接,但是需要引导被攻击者去访问触发攻击脚本,当被触发后不可二次触发。
存储型XSS:持久化攻击,恶意攻击者通过Web界面的可编辑存储功能向服务器插入恶意代码。如留言板,个人信息编辑功能等等。每当被攻击者访问该功能界面都会触发恶意代码。
DOM型XSS:DOM型XSS其实是一种特殊类型的反射型XSS,它是基于DOM文档对象模型的一种漏洞。允许程序或者脚本动态的访问或者更新文档内容,经过DOM解析触发XSS攻击,DOM型XSS不同与反射型XSS和存储型XSS,DOM型XSS不需要服务端参与解析响应,只是通过浏览器端的DOM进行解析。
漏洞原理
从白盒的角度上分别解析下三类漏洞。
反射型XSS示例代码如下(图 2)
(图 2)
前端提供了表单的输入,通过GET请求提交传入的xss参数,后端对截取的xss参数进行输出,由于没有对用户输入的xss参数未进行过滤,可直接构造XSS攻击脚本触发。
存储型XSS示例代码如下:(图 3)
(图 3)
前端表单可传入xss参数,将传入的xss参数保存在数据库中,在触发端界面调用数据库的内容,我们看到输入输出端都没有进行有效的过滤,则构造XSS攻击脚本存储在服务器,在前端界面调用该内容,造成XSS漏洞。
DOM型XSS示例代码如下:(图 4)
(图 4)
后端将传入的name值重新定义,在前端输出部分重新定义一个属性,该属性可能是一个带有JS代码的事件处理程序。将获取text的值输出在print内,而text的值有来源于name,DOM-XSS 的数据流向是:URL-->浏览器,前后端均没有对传入的参数进行过滤控制,则构造XSS攻击脚本可直接触发XSS攻击。
漏洞输出形式
根据上面XSS定义的阐述,我们可以得知XSS漏洞的产生是由于没有控制用户的输入,不管是存储型XSS还是反射型XSS。反射型XSS通常参数可被用户定义,且该参数的值能够回显到HTML中。通常出现在网站查询功能等。存储型XSS一般可提交内容保存到服务器,如个人信息修改,论坛发帖,留言等等。
至于XSS的输出,主要分以下两种情况,「结果输出在标签之间」,「结果输出在标签之内」,如图5,输出的内容显示在<pre>标签之间。
(图 5)
另外一种情况为「输出的内容在标签之内」,如图6,输出的结果在<input>标签内部。
(图 6)
两种结果输出方式决定了如何去构造XSS攻击脚本。「结果输出在标签之间」可直接构造攻击脚本;「结果输出在标签之内」,如果要触发XSS攻击,需要闭合语句再构造语句,如图6所示,需要闭合语句,闭合方式和SQL注入较为类似。
如何让区分「结果输出语句在标签内」和「结果输出语句在标签外」,查看该浏览器输出段的源代码,以查询功能为例,随意输入内容,查看该内容在前端源代码的显示位,(图7)我们可以看到结果输出位在标签之间。
(图 7)
漏洞挖掘
判断XSS的输出方式,根据输出方式选择XSS的构造方法。一些网站在开发时会对一些传参进行简单的控制,如使用黑名单的方法控制过滤用户传入的参数。如图8为例,对传入的数据进行正则匹配过滤,函数用于正则表达式的搜索和替换,这使得双写绕过、大小写混淆绕过不再有效或过滤敏感的关键词。
(图 8)
(图 9)
『黑名单』本身就是存在缺陷的,从上面的代码可以发现,如果构造的脚本不触发黑名单的规则,则攻击仍会执行。以图8为例,虽然对script对大小写混写,双写绕过进行了绕过,但是并未对其他的标签进行过滤,如<a><img><svg>等标签进行安全防护,图9等同,虽然过滤了大部分的常见关键词,但是仍然存在很多的遗漏。如触发事件on系列就存在以下很多种的触发方式(图10),如遇到以上过滤,可尝试不同的触发事件进行绕过。
(图 10)
针对黑名单过滤,主要以尝试为主,尽可能的找寻黑名单以外的标签或事件。除了全面的尝试,还存在其它可绕过XSS过滤的方法。
首先说一下HTML实体编码绕过,在 HTML 中,某些字符是预留的。是用一个编号写入 HTML代码中来代替一个字符,在使用浏览器访问网页时会将这个编号解析还原为字符以供阅读,如果过滤了<>,我们可以使用HTML实体编码绕过(图11)。
(图 11)
以十进制,或十六进制等进行编码,十六进制需要以&#x开头。假设alert被过滤,Payload即可使用十进制进行编码。
<img/src=1 onerror=alert(1)>
但是如果XSS的参数在GET请求中,需要注意的是,&字符以及#字符在URL的解析中都是存在特殊含义的,所以需要对该字符进行URL编码。
当然还可以使用字符集进行绕过,如UTF-7,US-ascll,UTF-16等浏览器支持的字符集进行payload的编码。如使用UTF-7编码攻击脚本。
%2BADw-script%2BAD4-alert%281%29%2BADw-/script%2BAD4-
还可以使用JS转义,假使过滤了alert这个关键词。使用Unicode转义关键字中的字符,如使用l的unicode编码\u006c,构造的攻击脚本。
<script>a\u006cert(1);</script>
另一个可以绕过的就是空字符,空字符 (%00) 使得过滤器不能看到完整的 <SCRIPT> 标签,但是只适用 IE 6.0, IE 7.0 。
<SCR%00IPT>alert("XSS")</SCRIPT>
使用evel函数来动态构造字符串。
<script>evel(‘al’+’ert’(1))<script>
当然还可以适当的使用空格回车以及TAB键进行插入,执行成功主要是依托与JS的特性,JS主要以分号结尾,如果执行时碰到TAB等会继续向下执行,直至分号结尾,构造出Payload。
<img/src=javascript:alert(1)>
大小写混写有的时候也许也是一线生机。
<ImG/SrC=1 OnerrOr=alErt(1)>
有时还会碰到这种情况,当闭合语句时,输入单引号,双引号,但是却回显为\’,\”。这种情况一般都出现在PHP的网站中,主要是因为PHP中开启了magic_quotes_gpc,开启后会把一些特殊字符进行轮换,这个时候我们可以使用javascript中的String.fromCharCode(...)进行绕过。假设alert被过滤,可使用String.fromCharCode(...)进行绕过,tring.fromCharCode()是javascript中的字符串方法,用来把ASCII转换为字符串构造payload。
<script>String.fromCharCode(97, 108, 101, 114, 116, 40, 34, 88,83, 83, 34, 41, 59)</script>
使用base64进行编码,构造出的payload如下
<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">
在构造一些XSS攻击脚本时会调用空格,如空格被过滤可使用/**/进行替换
<img/src=1/**/onload=alert(1)>
测试思路
上面介绍了方法,总结一下如何去挖掘XSS。
以搜索框为例,可以先尝试输入一些闭合字符检测程序是否会报错,或者直接插入标签检测是否响应。闭合字符为单引号’,双引号”,尖括号<>等等,直接插入标签,如<br><h1>标签等等。闭合字符主要对应的场景是「结果输出在标签内」,直接插入标签对应的场景是「结果输出在标签外」。
比较常用的一个检测字符串'';!--"<XSS>=&{()}。可以用此字符串检测是否闭合以及页面是否异常。
接下来插入带有攻击载荷的标签,也可以插入<h1>标签进行检测。插入后检测标签是否响应,接下来再输入攻击脚本,检测标签是否被过滤,如若被过滤可更换各类标签、事件等或对Payload进行编码混淆处理。
工具介绍
进行手动XSS的挖掘可以配合使用HackBar去进行XSS检测,Hackbar提供了几种常见XSS的编码混淆方式。图12为Hackbar界面。
(图 12)
当然也可以选择更方便的自动化检测工具,如XSStrike、XSSFork、XSSer等等,可直接进行XSS的扫描探测。图13为XSStrike的操作界面。
(图 13)
XSS防御
至于XSS的防御,刚才也大概的提及到几点,目前XSS的主要防御方法是对输入(和URL参数)进行过滤,对输出进行编码。如没有类型要求的话,尽可能的使用白名单去防御XSS。针对Cookie的安全防护,可以使用HttpOnly属性去进行安全控制。