购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.2 XSS攻击进阶

3.2.1 初探XSS Payload

前文谈到了XSS的几种分类。接下来,就从攻击的角度来体验一下XSS的威力。

XSS攻击成功后,攻击者能够对用户当前浏览的页面植入恶意脚本,通过恶意脚本,控制用户的浏览器。这些用以完成各种具体功能的恶意脚本,被称为“XSS Payload”。

XSS Pay load实际上就是JavaScript脚本(还可以是Flash或其他富客户端的脚本),所以任何JavaScript脚本能实现的功能,XSS Payload都能做到。

一个最常见的XSS Payload,就是通过读取浏览器的Cookie对象,从而发起“Cookie劫持”攻击。

Cookie中一般加密保存了当前用户的登录凭证。Cookie如果丢失,往往意味着用户的登录凭证丢失。换句话说,攻击者可以不通过密码,而直接登录进用户的账户。

如下所示,攻击者先加载一个远程脚本:

真正的XSS Payload写在这个远程脚本中,避免直接在URL的参数里写入大量的JavaScript代码。

在evil.js 中,可以通过如下代码窃取Cookie:

这段代码在页面中插入了一张看不见的图片,同时把document.cookie对象作为参数发送到远程服务器。

事实上,http://www.evil.com/log并不一定要存在,因为这个请求会在远程服务器的Web日志中留下记录:

这样,就完成了一个最简单的窃取Cookie的XSS Payload。

如何利用窃取的Cookie登录目标用户的账户呢?这和“利用自定义Cookie访问网站”的过程是一样的,参考如下过程。

在Firefox中访问用户的百度空间,登录后查看当前的Cookie:

查看当前页面的Cookie值

然后打开IE,访问同一个页面。此时在IE中,用户是未登录状态:

用户处于未登录状态

将Firefox中登录后的Cookie记录下来,并以之替换当前IE中的Cookie。重新发送这个包:

使用同一Cookie值重新发包

通过返回的页面可以看到,此时已经登录进该账户:

返回登录后的状态页面

验证一下,把返回的HTML代码复制到本地打开后,可以看到右上角显示了账户信息相关的数据:

返回页面是已登录状态

所以,通过XSS攻击,可以完成“Cookie劫持”攻击,直接登录进用户的账户。

这是因为在当前的Web中,Cookie一般是用户登录的凭证,浏览器发起的所有请求都会自动带上Cookie。如果Cookie没有绑定客户端信息,当攻击者窃取了Cookie后,就可以不用密码登录进用户的账户。

Cookie的“HttpOnly”标识可以防止“Cookie劫持”,我们将在稍后的章节中再具体介绍。

3.2.2 强大的XSS Payload

上节演示了一个简单的窃取Cookie的XSS Payload。在本节中,将介绍一些更为强大的XSS Payload。

“Cookie劫持”并非所有的时候都会有效。有的网站可能会在Set-Cookie时给关键Cookie植入HttpOnly标识;有的网站则可能会把Cookie与客户端IP绑定(相关内容在“XSS的防御”一节中会具体介绍),从而使得XSS窃取的Cookie失去意义。

尽管如此,在XSS攻击成功后,攻击者仍然有许多方式能够控制用户的浏览器。

3.2.2.1 构造GET与POST请求

一个网站的应用,只需要接受HTTP协议中的GET或POST请求,即可完成所有操作。对于攻击者来说,仅通过JavaScript,就可以让浏览器发起这两种请求。

比如在Sohu博客上有一篇文章,想通过XSS删除它,该如何做呢?

Sohu博客页面

假设Sohu博客所在域的某页面存在XSS漏洞,那么通过JavaScript,这个过程如下。正常删除该文章的链接是:

对于攻击者来说,只需要知道文章的id,就能够通过这个请求删除这篇文章了。在本例中,文章的id是156713012。

攻击者可以通过插入一张图片来发起一个GET请求:

攻击者只需要让博客的作者执行这段JavaScript代码(XSS Payload),就会把这篇文章删除。在具体攻击中,攻击者将通过XSS诱使用户执行XSS Payload。

再看一个复杂点的例子。如果网站应用者接受POST请求,那么攻击者如何实施XSS攻击呢?

下例是Douban的一处表单。攻击者将通过JavaScript发出一个POST请求,提交此表单,最终发出一条新的消息。

在正常情况下,发出一条消息,浏览器发的包是:

Douban上发新消息的请求包

要模拟这一过程,有两种方法。第一种方法是,构造一个form表单,然后自动提交这个表单:

如果表单的参数很多的话,通过构造DOM节点的方式,代码将会非常冗长。所以可以直接写HTML代码,这样会使得整个代码精简很多,如下所示:

自动提交表单成功:

通过表单自动提交发消息成功

第二种方法是,通过XMLHttpRequest发送一个POST请求:

再次提交成功:

通过XMLHttpRequest发消息成功

通过这个例子可以清楚地看到,使用JavaScript模拟浏览器发包并不是一件困难的事情。

所以XSS攻击后,攻击者除了可以实施“Cookie劫持”外,还能够通过模拟GET、POST请求操作用户的浏览器。这在某些隔离环境中会非常有用,比如“Cookie劫持”失效时,或者目标用户的网络不能访问互联网等情况。

下面这个例子将演示如何通过XSS Payload 读取QMail用户的邮件文件夹。

首先看看正常的请求是如何获取到所有的邮件列表的。登录邮箱后,可以看到:

QQ邮箱的界面

点击“收件箱”后,看到邮件列表。抓包发现浏览器发出了如下请求:

QQ邮箱的邮件列表

经过分析发现,真正能访问到邮件列表的链接是:

在Firebug中分析QQ邮箱的页面内容

这里有一个无法直接构造出的参数值:sid。从字面推测,这个sid参数应该是用户ID加密后的值。

所以,XSS Payload的思路是先获取到sid的值,然后构造完整的URL,并使用XMLHttpRequest请求此URL,应该就能得到邮件列表了。XSS Payload如下:

执行这段代码后:

获取邮件内容

邮件列表的内容成功被XSS Payload获取到。

攻击者获取到邮件列表的内容后,还可以读取每封邮件的内容,并发送到远程服务器上。这只需要构造不同的GET或POST请求即可,在此不再赘述,有兴趣的读者可以自己通过JavaScript实现这个功能。

3.2.2.2 X SS钓鱼

XSS并非万能。在前文的例子中,XSS的攻击过程都是在浏览器中通过JavaScript脚本自动进行的,也就是说,缺少“与用户交互”的过程。

比如在前文提到的“通过POST表单发消息”的案例中,如果在提交表单时要求用户输入验证码,那么一般的XSS Payload都会失效;此外,在大多数“修改用户密码”的功能中,在提交新密码前,都会要求用户输入“Old Password”。而这个“Old Password”,对于攻击者来说,往往是不知道的。

但是,这就能限制住XSS攻击吗?答案是否定的。

对于验证码,XSS Payload可以通过读取页面内容,将验证码的图片URL发送到远程服务器上来实施——攻击者可以在远程XSS后台接收当前验证码,并将验证码的值返回给当前的XSS Payload,从而绕过验证码。

修改密码的问题稍微复杂点。为了窃取密码,攻击者可以将XSS与“钓鱼”相结合。

实现思路很简单:利用JavaScript在当前页面上“画出”一个伪造的登录框,当用户在登录框中输入用户名与密码后,其密码将被发送至黑客的服务器上。

通过JavaScript伪造的登录框

充分发挥想象力,可以使得XSS攻击的威力更加巨大。

3.2.2.3 识别用户浏览器

在很多时候,攻击者为了获取更大的利益,往往需要准确地收集用户的个人信息。比如,如果知道用户使用的浏览器、操作系统,攻击者就有可能实施一次精准的浏览器内存攻击,最终给用户电脑植入一个木马。XSS能够帮助攻击者快速达到收集信息的目的。

如何通过JavaScript脚本识别浏览器版本呢?最直接的莫过于通过XSS读取浏览器的UserAgent对象:

浏览器的UserAgent对象

这个对象,告诉我们很多客户端的信息:

但是浏览器的UserAgent是可以伪造的。比如,Firefox有很多扩展可以屏蔽或自定义浏览器发送的UserAgent。所以通过JavaScript取出来的这个浏览器对象,信息并不一定准确。

但对于攻击者来说,还有另外一种技巧,可以更准确地识别用户的浏览器版本。

由于浏览器之间的实现存在差异——不同的浏览器会各自实现一些独特的功能,而同一个浏览器的不同版本之间也可能会有细微差别。所以通过分辨这些浏览器之间的差异,就能准确地判断出浏览器版本,而几乎不会误报。这种方法比读取UserAgent要准确得多。

参考以下代码:

这段代码,找到了几个浏览器独有的对象,能够识别浏览器的大版本。依据这个思路,还可以找到更多“独特的”浏览器对象。

安全研究者Gareth Heyes曾经找到一种更巧妙的方法 ,通过很精简的代码,即可识别出不同的浏览器。

精简为一行代码,即:

3.2.2.4 识别用户安装的软件

知道了用户使用的浏览器、操作系统后,进一步可以识别用户安装的软件。

在IE中,可以通过判断ActiveX控件的classid是否存在,来推测用户是否安装了该软件。这种方法很早就被用于“挂马攻击”——黑客通过判断用户安装的软件,选择对应的浏览器漏洞,最终达到植入木马的目的。

看如下代码:

这段代码检测迅雷的一个控件(“XunLeiBHO.ThunderIEHelper”)是否存在。如果用户安装了迅雷软件,则默认也会安装此控件。因此通过判断此控件,即可推测用户安装了迅雷软件的可能性。

通过收集常见软件的classid,就可以扫描出用户电脑中安装的软件列表,甚至包括软件的版本。

一些第三方软件也可能会泄露一些信息。比如Flash有一个system.capabilities对象,能够查询客户端电脑中的硬件信息:

Flash的system.capabilities对象

在XSS Payload中使用时,可以在Flash的ActionScript中读取system.capabilities对象后,将结果通过 ExternalInterface 传给页面的JavaScript。这个过程在此不再赘述了。

浏览器的扩展和插件也能被XSS Payload扫描出来。比如对于Firefox的插件和扩展,有着不同的检测方法。

Firefox的插件(Plugins)列表存放在一个DOM对象中,通过查询DOM可以遍历出所有的插件:

Firefox的plugins对象

所以直接查询“navigator.plugins”对象,就能找到所有的插件了。在上图中所示的插件是“navigator.plugins[0]”。

而Firefox的扩展(Extension)要复杂一些。有安全研究者想出了一个方法:通过检测扩展的图标,来判断某个特定的扩展是否存在。

在Firefox中有一个特殊的协议:chrome://,Firefox的扩展图标可以通过这个协议被访问到。比如Flash Got扩展的图标,可以这样访问:

扫描Firefox扩展时,只需在JavaScript中加载这张图片,如果加载成功,则扩展存在;反之,扩展不存在。

3.2.2.5 CSS History Hack

我们再看看另外一个有趣的XSS Payload——通过CSS,来发现一个用户曾经访问过的网站。

这个技巧最早被Jeremiah Grossman发现,其原理是利用style的visited属性——如果用户曾经访问过某个链接,那么这个链接的颜色会变得与众不同:

浏览器会将点击过的链接示以不同的颜色:

安全研究者Rsnake公布了一个POC ,其效果如下:

Rsnake演示的攻击效果

红色标记的,就是用户曾经访问过的网站(即Visited下的两个网站)。

这个POC代码如下:

但是Firefox在2010年3月底决定修补这个问题,因此,未来这种信息泄露的问题可能在Mozilla浏览器中不会再继续存在了。

3.2.2.6 获取用户的真实IP地址

通过XSS Payload还有办法获取一些客户端的本地IP地址。

很多时候,用户电脑使用了代理服务器,或者在局域网中隐藏在NAT后面。网站看到的客户端IP地址,是内网的出口IP地址,而并非用户电脑真实的本地IP地址。如何才能知道用户的本地IP地址呢?

JavaScript本身并没有提供获取本地IP地址的能力,有没有其他办法?一般来说,XSS攻击需要借助第三方软件来完成。比如,客户端安装了Java环境(JRE),那么XSS就可以通过调用Java Applet的接口获取客户端的本地IP地址。

在XSS攻击框架“Attack API”中,就有一个获取本地IP地址的API:

此外,还有两个利用Java获取本地网络信息的API:

这种方法需要攻击者写一个Java Class,嵌入到当前页面中。除了Java之外,一些ActiveX控件可能也会提供接口查询本地IP地址。这些功能比较特殊,需要根据具体情况具体分析,这里不赘述了。

Metasploit引擎曾展示过一个强大的测试页面,综合了Java Applet、Flash、iTunes、Office Word、QuickTime等第三方软件的功能,抓取用户的本地信息 ,有兴趣深入研究的读者可以参考。

3.2.3 XSS 攻击平台

XSS Payload如此强大,为了使用方便,有安全研究者将许多功能封装起来,成为XSS攻击平台。这些攻击平台的主要目的是为了演示XSS的危害,以及方便渗透测试使用。下面就介绍几个常见的XSS攻击平台。

Attack API

Attack API 是安全研究者pdp所主导的一个项目,它总结了很多能够直接使用XSS Payload,归纳为API的方式。比如上节提到的“获取客户端本地信息的API”就出自这个项目。

BeEF

BeEF 曾经是最好的XSS演示平台。不同于Attack API,BeEF所演示的是一个完整的XSS攻击过程。BeEF有一个控制后台,攻击者可以在后台控制前端的一切。

BeFF的后台界面

每个被XSS攻击的用户都将出现在后台,后台控制者可以控制这些浏览器的行为,并可以通过XSS向这些用户发送命令。

XSS-Proxy

XSS-Proxy是一个轻量级的XSS攻击平台,通过嵌套iframe的方式可以实时地远程控制被XSS攻击的浏览器。

XSS-Proxy的实现原理

这些XSS攻击平台有助于深入理解XSS的原理和危害。

3.2.4 终极武器:XSS Worm

XSS也能形成蠕虫吗?我们知道,以往的蠕虫是利用服务器端软件漏洞进行传播的。比如2003年的冲击波蠕虫,利用的是Windows的RPC远程溢出漏洞。

3.2.4.1 Samy Worm

在2005年,年仅19岁的Samy Kamkar发起了对MySpace.com的XSS Worm攻击。Samy Kamkar的蠕虫在短短几小时内就感染了100万用户——它在每个用户的自我简介后边加了一句话:“but most of all, Samy is my hero.”(Samy是我的偶像)。这是Web安全史上第一个重量级的XSS Worm,具有里程碑意义。

今天我们看看当时的Samy蠕虫都做了些什么?

首先,MySpace过滤了很多危险的HTML标签,只保留了<a>标签、<img>标签、<div>标签等“安全的标签”。所有的事件比如“onclick”等也被过滤了。但是MySpace却允许用户控制标签的style属性,通过style,还是有办法构造出XSS的。比如:

其次,MySpace同时还过滤了“javascript”、“onreadystatechange”等敏感词,所以Samy用了“拆分法”绕过这些限制。

最后,Samy通过AJAX构造的POST请求,完成了在用户的heros列表里添加自己名字的功能;同时复制蠕虫自身进行传播。至此,XSS Worm就完成了。有兴趣的读者可以参考Samy蠕虫的技术细节分析

下面附上Samy Worm的源代码。这是具有里程碑意义的第一个XSS Worm,原本的代码压缩在一行内。为了方便阅读,如下代码已经经过了整理和美化。

XSS Worm是XSS的一种终极利用方式,它的破坏力和影响力是巨大的。但是发起XSS Worm攻击也有一定的条件。

一般来说,用户之间发生交互行为的页面,如果存在存储型XSS,则比较容易发起XSS Worm攻击。

比如,发送站内信、用户留言等页面,都是XSS Worm的高发区,需要重点关注。而相对的,如果一个页面只能由用户个人查看,比如“用户个人资料设置”页面,因为缺乏用户之间互动的功能,所以即使存在XSS,也不能被用于XSS Worm的传播。

3.2.4.2 百度空间蠕虫

下面这个XSS Worm的案例来自百度。

2007年12月,百度空间的用户忽然互相之间开始转发垃圾短消息,后来百度工程师紧急修复了这一漏洞:

百度空间的XSS蠕虫公告

这次事件,是由XSS Worm造成的。时任百度系统部高级安全顾问的方小顿,分析了这个蠕虫的技术细节,他在文中 写到:

上面基本就是代码,总体来说,还是很有意思的。

首先就是漏洞,过滤多一个字符都不行,甚至挪一个位置都不行(上面的Playload部分)。这个虫子比较特殊的地方是感染IE用户,对其他用户无影响;另外就是完全可以隐蔽地传播,因为只是在CSS中加代码并不会有什么明显的地方,唯一的缺陷是有点卡。所以,完全可以长时间地存在,感染面不限制于blog,存在CSS的地方都可以,譬如Profile。

另外比较强大的一点就是跟真正的虫子一样,不只是被动地等待,选择在好友发消息时引诱别人过来访问自己的blog,利用好奇心可以做到这点。

最后还加了个给在线人随机发消息请求加链接,威力可能更大,因为会创造比较大的基数,这样一感染就是一个blog。

到Baidu封锁时,这个虫子已经感染了8700多个blog。总体来说还不错,本来想作为元旦的一个贺礼,不过还是提前死掉了。可以看到,在代码和流程里运用了很多系统本身就有的特性,自己挖掘吧。

这个百度XSS Worm的源代码如下:

后来又增加了一个传播函数,不过那个时候百度已经开始屏蔽此蠕虫了:

攻击者想要通过XSS做坏事是很容易的,而XSS Worm则能够把这种破坏无限扩大,这正是大型网站所特别担心的事情。

无论是MySpace蠕虫,还是百度空间的蠕虫,都是“善意”的蠕虫,它们只是在“恶作剧”,而没有真正形成破坏。真正可怕的蠕虫,是那些在无声无息地窃取用户数据、骗取密码的“恶意”蠕虫,这些蠕虫并不会干扰用户的正常使用,非常隐蔽。

3.2.5 调试JavaScript

要想写好XSS Payload,需要有很好的JavaScript功底,调试JavaScript是必不可少的技能。在这里,就简单介绍几个常用的调试JavaScript的工具,以及辅助测试的工具。

Firebug

这是最常用的脚本调试工具,前端工程师与Web Hacking必备,被喻为“居家旅行的瑞士军刀”。

Firebug非常强大,它有好几个面板,可以查看页面的DOM节点。

Firebug的界面

调试JavaScript:

在Firebug中调试JavaScript

查看HTML与CSS:

在Firebug中查看HTML与CSS

毋庸置疑,Firebug是JavaScript调试的第一利器。如果要说缺点,那就是除了Firefox外,对其他浏览器的支持并不好。

IE 8 Developer Tools

在IE 8中,为开发者内置了一个JavaScript Debugger,可以动态调试JavaScript。

IE 8的开发者工具界面

在需要调试IE而又没有其他可用的JavaScript Debugger时,IE 8 Developer Tools是个不错的选择。

Fiddler

Fiddler 是一个本地代理服务器,需要将浏览器设置为使用本地代理服务器上网才可使用。Fiddler会监控所有的浏览器请求,并有能力在浏览器请求中插入数据。

Fiddler支持脚本编程,一个强大的Fiddler脚本将非常有助于安全测试。

Fiddler的界面

HttpWatch

HttpWatch是一个商业软件,它以插件的形式内嵌在浏览器中。

HttpWatch的界面

HttpWatch也能够监控所有的浏览器请求,在目标网站是HTTPS时会特别有用。但HttpWatch并不能调试JavaScript,它仅仅是一个专业的针对Web的“Sniffer”。

善用这些调试工具,在编写XSS Payload与分析浏览器安全时,会事半功倍。

3.2.6 XSS构造技巧

前文重点描述了XSS攻击的巨大威力,但是在实际环境中,XSS的利用技巧比较复杂。本章将介绍一些常见的XSS攻击技巧,也是网站在设计安全方案时需要注意的地方。

3.2.6.1 利用字符编码

“百度搜藏” 曾经出现过一个这样的XSS漏洞。百度在一个<script>标签中输出了一个变量,其中转义了双引号:

一般来说,这里是没有XSS漏洞的,因为变量处于双引号之内,系统转义了双引号导致变量无法“escape”。

但是,百度的返回页面是 GBK/GB 2312编码的,因此“%c1\”这两个字符组合在一起后,会成为一个Unicode字符。在Firefox下会认为这是一个字符,所以构造:

并提交:

提交的数据包

在Firefox下得到如下效果:

在Firefox下的效果

这两个字节:“%c1\”组成了一个新的Unicode字符,“%c1”把转义符号“\”给“吃掉了”,从而绕过了系统的安全检查,成功实施了XSS攻击。

3.2.6.2 绕过长度限制

很多时候,产生XSS的地方会有变量的长度限制,这个限制可能是服务器端逻辑造成的。假设下面代码存在一个XSS漏洞:

服务器端如果对输出变量“$var”做了严格的长度限制,那么攻击者可能会这样构造XSS:

希望达到的输出效果是:

假设长度限制为20个字节,则这段XSS会被切割为:

连一个完整的函数都无法写完,XSS攻击可能无法成功。那此时,是不是万事大吉了呢?答案是否定的。

攻击者可以利用事件(Event)来缩短所需要的字节数:

加上空格符,刚好够20个字节,实际输出为:

当用户点击了文本框后,alert()将执行:

恶意脚本被执行

但利用“事件”能够缩短的字节数是有限的。最好的办法是把XSS Payload写到别处,再通过简短的代码加载这段XSS Payload。

最常用的一个“藏代码”的地方,就是“location.hash”。而且根据HTTP协议,location.hash的内容不会在HTTP包中发送,所以服务器端的Web日志中并不会记录下location.hash里的内容,从而也更好地隐藏了黑客真实的意图。

总共是40个字节。输出后的HTML是:

因为location.hash的第一个字符是 # ,所以必须去除第一个字符才行。此时构造出的XSS URL为:

用户点击文本框时,location.hash里的代码执行了。

location.hash里的脚本被执行

location.hash本身没有长度限制,但是浏览器的地址栏是有长度限制的,不过这个长度已经足够写很长的XSS Payload了。要是地址栏的长度也不够用,还可以再使用加载远程JS的方法,来写更多的代码。

在某些环境下,可以 利用注释符绕过长度限制

比如我们能控制两个文本框,第二个文本框允许写入更多的字节。此时可以利用HTML的“注释符号”,把两个文本框之间的HTML代码全部注释掉,从而“打通”两个<input>标签。

在第一个input框中,输入:

在第二个input框中,输入:

最终的效果是:

中间的代码全部被

给注释掉了!最终效果如下:

恶意脚本被执行

而在第一个input框中,只用到了短短的6个字节!

3.2.6.3 使用<base>标签

<base>标签并不常用,它的作用是定义页面上的所有使用“相对路径”标签的hosting地址。

比如,打开一张不存在的图片:

测试页面

这张图片实际上是Google的一张图片,原地址为:

在<img>标签前加入一个<base>标签:

<base>标签将指定其后的标签默认从“http://www.google.com”取URL:

测试页面

图片被找到了。

需要特别注意的是,在有的技术文档中,提到<base>标签只能用于<head>标签之内,其实这是不对的。<base>标签可以出现在页面的任何地方,并作用于位于该标签之后的所有标签。

攻击者如果在页面中插入了<base>标签,就可以通过在远程服务器上伪造图片、链接或脚本,劫持当前页面中的所有使用“相对路径”的标签。比如:

所以在设计XSS安全方案时,一定要过滤掉这个非常危险的标签。

3.2.6.4 window.name的妙用

window.name对象是一个很神奇的东西。对当前窗口的window.name对象赋值,没有特殊字符的限制。因为window对象是浏览器的窗体,而并非document对象,因此很多时候window对象不受同源策略的限制。攻击者利用这个对象,可以实现跨域、跨页面传递数据。在某些环境下,这种特性将变得非常有用。

参考以下案例。假设“www.a.com/test.html”的代码为:

这段代码将window.name赋值为test,然后显示当前域和window.name的值,最后将页面跳转到“www.b.com/test1.html”。

“www.b.com/test1.html”的代码为:

这里显示了当前域和window.name的值。最终效果如下,访问“www.a.com/test.html”:

测试页面

window.name赋值成功,然后页面自动跳转到“www.b.com/test1.html”:

测试页面

这个过程实现数据的跨域传递:“test”这个值从www.a.com传递到www.b.com。

使用window.name可以缩短XSS Payload的长度,如下所示:

在同一窗口打开XSS的站点后,只需通过XSS执行以下代码即可:

只有11个字节,短到了极点。

这个技巧为安全研究者luoluo所发现,同时他还整理了很多绕过XSS长度限制的技巧

3.2.7 变废为宝:Mission Impossible

从XSS漏洞利用的角度来看,存储型XSS对攻击者的用处比反射型XSS要大。因为存储型XSS在用户访问正常URL时会自动触发;而反射型XSS会修改一个正常的URL,一般要求攻击者将XSS URL发送给用户点击,无形中提高了攻击的门槛。

而有的XSS漏洞,则被认为只能够攻击自己,属于“鸡肋”漏洞。但随着时间的推移,数个曾经被认为是无法利用的XSS漏洞,都被人找到了利用方法。

3.2.7.1 Apache Expect Header XSS

“Apache Expect Header XSS”漏洞最早公布于2006年。这个漏洞曾一度被认为是无法利用的,所以厂商不认为这是个漏洞。这个漏洞的影响范围是:Apache Httpd Server版本1.3.34、2.0.57、2.2.1及以下。漏洞利用过程如下。

向服务器提交:

服务器返回:

注意到服务器在出错返回时,会把Expect头的内容未经任何处理便写入到页面中,因此Expect头中的HTML代码就被浏览器解析执行了。

这是Apache的漏洞,影响范围相当广。从这个攻击过程可以看出,需要在提交请求时向HTTP头中注入恶意数据,才能触发这个漏洞。但对于XSS攻击来说,JavaScript工作在渲染后的浏览器环境中,无法控制用户浏览器发出的HTTP头。因此,这个漏洞曾经一度被认为是“鸡肋”漏洞。

后来安全研究者Amit Klein提出了“使用Flash构造请求”的方法,成功地利用了这个漏洞,变废为宝!

在Flash中发送HTTP请求时,可以自定义大多数的HTTP头。如下是Amit Klein的演示代码:

正因为此,Flash在新版本中禁止用户自定义发送Expect头。但后来发现可以通过注入HTTP头的方式绕过这个限制:

目前Flash已经修补好了这些问题。

此类攻击,还可以通过Java Applet等构造HTTP请求的第三方插件来实现。

3.2.7.2 Anehta的回旋镖

反射型XSS也有可能像存储型XSS一样利用:将要利用的反射型XSS嵌入一个存储型XSS中。这个攻击技巧,曾经在笔者实现的一个XSS攻击平台(Anehta)中使用过,笔者将其命名为“回旋镖”。

因为浏览器同源策略的原因,XSS也受到同源策略的限制——发生在A域上的XSS很难影响到B域的用户。

回旋镖的思路就是:如果在B域上存在一个反射型“XSS_B”,在A域上存在一个存储型“XSS_A”,当用户访问A域上的“XSS_A”时,同时嵌入B域上的“XSS_B”,则可以达到在A域的XSS攻击B域用户的目的。

我们知道,在IE中,<iframe>、<img>、<link>等标签都会拦截“第三方Cookie”的发送,而在Firefox中则无这种限制(第三方Cookie即指保存在本地的Cookie,也就是服务器设置了expire时间的Cookie)。

所以,对于Firefox来说,要实现回旋镖的效果非常简单,只需要在XSS_A处嵌入一个iframe即可:

但是对于IE来说,则要麻烦很多。为了达到执行XSS_B的目的,可以使用一个<form>标签,在浏览器提交form表单时,并不会拦截第三方Cookie的发送。

因此,先在XSS_A上写入一个<form>,自动提交到XSS_B,然后在XSS_B中再跳转回原来的XSS_A,即完成一个“回旋镖”的过程。但是这种攻击的缺点是,尽管跳转花费的时间很短,但用户还是会看到浏览器地址栏的变化。

代码如下:

如果能在B域上找到一个302跳转的页面,也可以不使用form表单,这样会更加方便。

虽然“回旋镖”并不是一种完美的漏洞利用方式,但也能将反射型XSS的效果变得更加自动化。

XSS漏洞是一个Web安全问题,不能因为它的利用难易程度而决定是否应该修补。随着技术的发展,某些难以利用的漏洞,也许不再是难题。

3.2.8 容易被忽视的角落:Flash XSS

前文讲到的XSS攻击都是基于HTML的,其实在Flash中同样也有可能造成XSS攻击。

在Flash中是可以嵌入ActionScript脚本的。一个最常见的Flash XSS可以这样写:

将Flash嵌入页面中:

ActionScript是一种非常强大和灵活的脚本,甚至可以使用它发起网络连接,因此应该尽可能地禁止用户能够上传或加载自定义的Flash文件。

由于Flash文件如此危险,所以在实现XSS Filter时,一般都会禁用<embed>、<object>等标签。后者甚至可以加载ActiveX控件,能够产生更为严重的后果。

如果网站的应用一定要使用Flash怎么办?一般来说,如果仅仅是视频文件,则要求转码为“flv文件”。flv文件是静态文件,不会产生安全隐患。如果是带动态脚本的Flash,则可以通过Flash的配置参数进行限制。

常见的嵌入Flash的代码如下:

限制Flash动态脚本的最重要的参数是“allowScriptAccess”,这个参数定义了Flash能否与HTML页面进行通信。它有三个可选值:

always,对与HTML的通信也就是执行JavaScript不做任何限制;

sameDomain,只允许来自于本域的Flash与Html通信,这是默认值;

never,绝对禁止Flash与页面通信。

使用always是非常危险的,一般推荐使用never。如果值为sameDomain的话,请务必确保Flash文件不是用户传上来的。

除了“allowScriptAccess”外,“allowNetworking”也非常关键,这个参数能控制Flash与外部网络进行通信。它有三个可选值:

all,允许使用所有的网络通信,也是默认值;

internal,Flash不能与浏览器通信如navigateToURL,但是可以调用其他的API;

none,禁止任何的网络通信。

一般建议此值设置为none或者internal。设置为all可能带来安全问题。

除了用户的Flash文件能够实施脚本攻击外,一些Flash也可能会产生XSS漏洞。看如下ActionScript代码:

这段代码经常出现在广告的Flash中,用于控制用户点击后的URL。但是这段代码缺乏输入验证,可以被XSS攻击:

安全研究者Stefano Di Paola曾经写了一个叫“SWFIntruder” 的工具来检测产生在Flash里的XSS漏洞,通过这个工具可以检测出很多注入Flash变量导致的XSS问题。

SWFIntruder的界面

要修补本例中的漏洞,可以使用输入检查的方法:

Flash XSS往往被开发者所忽视。注入Flash变量的XSS,因为其问题出现在编译后的Flash文件中,一般的扫描工具或者代码审计工具都难以检查,常常使其成为漏网之鱼。

OWASP为Flash安全研究设立了一个Wiki页面 ,有兴趣的读者可以参考。

3.2.9 真的高枕无忧吗:JavaScript开发框架

在Web前端开发中,一些JavaScript开发框架深受开发者欢迎。利用JavaScript开发框架中的各种强大功能,可以快速而简洁地完成前端开发。

一般来说,成熟的JavaScript开发框架都会注意自身的安全问题。但是代码是人写的,高手偶尔也会犯错。一些JavaScript开发框架也曾暴露过一些XSS漏洞。

Dojo

Dojo是一个流行的JavaScript开发框架,它曾被发现存在XSS漏洞。在Dojo 1.4.1中,存在两个“DOM Based XSS”:

用户输入由theme参数传入,然后被赋值给变量themeCss,最终被document.write到页面里:

所以凡是引用了_testCommon.js的文件,都受影响。POC如下:

类似的问题还存在于:

它也是从window.location传入了用户能够控制的数据,最终被document.write到页面:

POC如下:

这些问题在Dojo 1.4.2版本中已经得到修补。但是从这些漏洞可以看到,使用JavaScript开发框架也并非高枕无忧,需要随时关注可能出现的安全问题。

YUI

翻翻YUI的bugtracker,也可以看到类似Dojo的问题。

在YUI 2.8.1中曾经fix过一个“DOM Based XSS”。YUI的History Manager功能中有这样一个问题,打开官方的demo页:

点击一个Tab页,等待页面加载完成后,在URL的hash中插入恶意脚本。构造的XSS如下:

脚本将得到执行。其原因是在history.js的_updateIframe方法中信任了用户可控制的变量:

最后被写入到页面导致脚本执行。YUI的修补方案是对变量进行了htmlEscape。

jQuery

jQuery可能是目前最流行的JavaScript框架。它本身出现的XSS漏洞很少。但是开发者应该记住的是,JavaScript框架只是对JavaScript语言本身的封装,并不能解决代码逻辑上产生的问题。所以开发者的意识才是安全编码的关键所在。

在jQuery中有一个html()方法。这个方法如果没有参数,就是读取一个DOM节点的innerHTML;如果有参数,则会把参数值写入该DOM节点的innerHTML中。这个过程中有可能产生“DOM Based XSS”:

如上,如果用户能够控制输入,则必然会产生XSS。在开发过程中需要注意这些问题。

使用JavaScript框架并不能让开发者高枕无忧,同样可能存在安全问题。除了需要关注框架本身的安全外,开发者还要提高安全意识,理解并正确地使用开发框架。 MQRQfIr8Ur+RqG5tkZAUUuN2mwkksdtHjtSM+PHxWNb4+eQjHVi5MY7IIRB2NbEE

点击中间区域
呼出菜单
上一章
目录
下一章
×