通过DOM污染劫持Service Worker的技术解析
Service Worker基础回顾
许多网站使用Service Worker(SW)提供缓存和离线功能。由于SW无法访问父页面的DOM,网页通常通过查询参数向SW传递信息。如果这些参数处理不当,可能导致恶意JavaScript在SW内部执行,正如《Service Worker跨站脚本安全研究》论文所指出的。虽然这类漏洞较为罕见,但其影响远大于普通XSS,因为它能实现永久性的客户端站点接管。
Service Worker劫持技术
劫持Service Worker的技术可实现三个关键目标:
- HTML过滤规避
- CSP绕过
- XSS升级
该漏洞的核心在于importScripts()
函数,它允许SW从不同域获取JavaScript。在以下示例中,攻击者可以控制host
查询参数,从而完全控制被导入的脚本,进而完全控制网站响应。
利用此类漏洞需要两个组件:
- 对传递给SW的查询字符串参数的控制权
- SW内部可受查询字符串参数影响的
importScripts()
函数调用
index.html:
|
|
sw.js:
|
|
使用Puppeteer和DOM Invader进行漏洞挖掘
基于这些知识,我们使用Puppeteer和DOM Invader进行漏洞挖掘。扫描多个漏洞赏金站点后,发现一个网站在iframe内使用SW,并从框架文档向SW传递URL参数。DOM Invader立即标记了此行为,但幸运的是该网站不允许从顶层窗口注入SW。
代码示例如下:
|
|
DOM Invader在location.href
源中生成随机令牌,然后传递给serviceWorker.register()
接收器。此行为随后在增强DOM中报告。我们配置DOM Invader自动向所有源注入canary,但这并未产生太多结果。因此我们决定采用另一种方法:查找所有使用查询参数的Service Worker注册?这将识别潜在易受攻击的SW,但需要进一步调查是否可利用——这导致了一个有趣的发现…
Service Worker污染攻击
我们发现一个主要网站使用<div>
元素向SW脚本传递信息。他们通过使用id为"cdnDomain"的<div>
元素的innerText
实现:
|
|
这种做法很危险,因为如果通过DOM污染技术污染变量,就可以控制SW域。实际上,这与正常的DOM污染攻击略有不同,因为代码使用document.getElementById()
和innerText
。如果在<div>
元素之前注入HTML元素,就可以控制CDN域——这意味着可以控制SW脚本的内容。这可能导致完全控制网站响应,同时绕过HTML过滤,或规避CSP并升级反射型XSS。
代码结构如下: 随后使用SW注册方法传递此域:
|
|
然后SW本身使用该域加载一些脚本:
|
|
最初我们认为需要在cdnDomain div之前有一个元素才能利用,但发现情况并非必然如此。如果注入具有相同id属性的<html>
或<body>
标签,可以污染document.getElementById()
调用的结果。示例:
|
|
有趣的是,可以从innerText
中隐藏元素,因此如果注入HTML/body标签,可以使用样式将其从innerText
中隐藏,防止其他文本干扰攻击:
|
|
我们还研究了SVG,可以在其中使用<body>
标签:
|
|
在Chrome和Firefox上,需要在SVG内使用<foreignobject>
标签才能使用HTML标签:
|
|
污染document.querySelector()
相同技术可用于污染document.querySelector()
的结果。由于此函数返回找到的第一个元素,可以使用<html>
或<body>
标签污染类名。
|
|
使用DOM Invader查找Service Worker注入
要查找SW注入,只需将canary放置在查询字符串中,或配置DOM Invader将canary注入所有源。然后DOM Invader如果发现易受攻击的函数调用,将显示名为"serviceWorker.register"的新接收器:
我们创建了一个测试用例来演示此问题。请注意,这仅标记查询字符串正在传递给SW,需要进一步调查此查询字符串是否在SW内部被解析并与importScripts()
之类的东西一起使用。
DOM Invader可以通过操纵查询字符串帮助您找到SW注入。然而,在更复杂的情况或漏洞链中,可能希望配置DOM Invader仅显示SW注册,并使用空白canary查看所有调用。可以通过转到设置并输入空白canary,然后单击"update canary"来实现。如果只想查看SW注册,再次单击设置,然后单击常规设置齿轮(DOM Invader旁边),向下滚动并选择无。然后搜索serviceWorker.register并启用它,这将显示所有SW注册。还可以使用接收器回调来查找接收器值中的问号。
要自行查找这些类型的漏洞,可以使用最新版本的Burp Suite,目前可在早期采用者频道上获得。如果您尝试了,特别是如果发现任何SW注入实例,请告诉我们——我们很乐意收到您的反馈。