dxa4481/cssInjection

GitHub: dxa4481/cssInjection

演示如何通过 CSS 属性选择器注入(无需 iFrame 和 XSS)逐字符窃取 CSRF token,利用弹出窗口与 Service Worker 实现纯客户端数据外泄。

Stars: 324 | Forks: 49

# 利用 CSS 注入窃取 CSRF token(无需 iFrames) [这里](https://www.curesec.com/blog/article/blog/Reading-Data-via-CSS-Injection-180.html)的一篇文章详细介绍了一种通过 CSS 注入,利用属性选择器和 iFrames 窃取敏感数据的方法。由于该方法需要使用 iFrames,而大多数主流网站都禁止被嵌入到框架中,因此这种攻击并不总是切实可行。 在这里,我将介绍一种无需 iFrames 的方法,可以在大约 10 秒内有效地窃取 CSRF token。 一旦 CSRF token 被窃取,由于受害者已经位于攻击者的网站上,攻击者就可以继续对用户发起完整的 CSRF 攻击。 ## 背景 正如原帖所述,[CSS 属性选择器](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)允许开发者根据属性标签值的子字符串匹配来选择元素。这些值选择器可以执行以下三种操作之一: + 如果字符串以该子字符串开头,则匹配 + 如果字符串以该子字符串结尾,则匹配 + 如果字符串在任意位置包含该子字符串,则匹配 其一个实际应用场景是,将所有以 "https://example.com" 开头的 `href` 属性设置为特殊颜色。 这带来的一个不幸的副作用是,敏感信息有时会被存储在 HTML 属性值中。最常见的是,CSRF token 就是以这种方式存储在隐藏表单的 value 属性中。 这允许我们将 CSS 选择器与目标表单上的属性进行匹配,并根据表单是否匹配开头的字符串来加载外部资源(例如背景图片),从而向攻击者指示第一个字符。 使用这种方法,攻击者可以逐个遍历字符串,并渗透出整个秘密值。 要实现这一点,受害者的服务器需要允许或存在渲染任意 CSS 的漏洞。这可能通过 CSS 注入发生,或者通过网站上允许你包含样式表的功能发生。注意:该网站不需要存在 XSS 漏洞。 为了渲染受害者的 CSS,原论文提议使用 iFrames。这种方法的局限性当然在于,目标网站是否禁止被嵌入到框架中。 此外,这还涉及空间/时间的权衡:要么同时并行加载所有可能的字符,要么每次只加载一个进行多路复用。在我的示例中,为了节省时间,我选择一次性全部加载。在某些注入空间较小的场景中,多路复用可能是更可行的选择。 ## 无需 iFrames 为了在没有 iFrames 的情况下实现这一点,我采用了我[之前](https://github.com/dxa4481/windowHijacking)讨论过的一种类似方法:创建一个弹出窗口,然后在设定的时间后更改弹出窗口的位置。 使用这种方法,我依然可以加载受害者的 CSS,但不再依赖于受害者网站是否允许被框架嵌入。由于初始弹出窗口是通过用户事件触发的,因此不会被浏览器阻止。 为了强制硬重载,我让弹出窗口在两次 CSS 注入之间加载一个虚拟窗口。如下所示: ``` var win2 = window.open('https://security.love/anything', 'f', "top=100000,left=100000,menubar=1,resizable=1,width=1,height=1") var win2 = window.open(`https://security.love/cssInjection/victim.html?injection=${css}`, 'f', "top=100000,left=100000,menubar=1,resizable=1,width=1,height=1") ``` ## 无需后端服务器 原论文描述了将数据渗透到后端服务器的过程,但由于 CSRF 是一种客户端攻击,如果我们能找到一种无需服务器即可完成此操作的方法,就能省去大量的开销和复杂性。 为了在客户端接收受害者的资源加载,我们可以利用 Service Worker,它能够拦截并读取请求数据。目前 Service Worker 仅适用于来自同源的请求,因此在我的演示中,我取巧地将受害者和攻击者页面放在了同一个源下。 不过不久之后,Chrome 可能会合并这项[实验性功能](https://developers.google.com/web/updates/2016/09/foreign-fetch),该功能允许 Service Worker 拦截跨域请求。 有了这项新增功能,我们可以让攻击 100% 在客户端完成,并迫使用户在点击链接后的 10 秒内执行 CSRF 操作,如下面的演示所示: ## 演示 如上所述,因为我不想运行 Web 服务器(GitHub Pages 就很好),所以我取巧地使用了 Service Worker 来拦截和模拟服务器端组件。因此,目前该演示仅适用于 Chrome。 首先,我创建了一个非常简单的受害者页面,其中包含一个基于 DOM 的 CSS 注入漏洞,并在页面上放置了一个敏感 token。我将其设为基于 DOM 的形式同样是为了消除对服务器的需求。你可能还会注意到,我还通过对小于号和大于号进行编码,加入了一些针对 script 标签注入的保护措施。 ```
``` 接下来,我们的攻击者会强制加载受害者的 CSS,并利用上述方法,逐个字符地窃取敏感 token。 在接收端,我定义了一个 Service Worker 来拦截请求,并通过 post-message 将其发送回当前域,然后我们将 token 存储在 local storage 中以备将来使用。你可以想象由一个后端 Web 服务器来承担此功能,并通过 WebSocket 或轮询将 CSRF token 发送回攻击者的域。 目前仅在 Chrome 中测试过: [演示](https://security.love/cssInjection/attacker.html) 如果一切正常,在点击页面上的某个位置后,你应该会看到 CSRF token 从受害者页面中被逐个字符地提取出来。 ## 总结思考 有趣的是,反射型 CSS 注入实际上比存储型 CSS 注入更具杀伤力,因为存储型 CSS 注入需要服务器在渲染前为受害者更新 CSS。 一段时间以来,CSS 注入的严重性一直存在争议。过去 IE 允许用户在 CSS 中执行 JavaScript。希望这个演示能够表明,CSS 注入以及在您的域上渲染不受信任的 CSS,依然可能导致严重的安全漏洞。
标签:CISA项目, CSRF, CSRF Token窃取, CSS属性选择器, CSS注入, Exfiltration, Web安全, Web安全实战, 前端安全, 后端开发, 多模态安全, 数据可视化, 数据窃取, 无iFrame攻击, 蓝队分析, 跨站请求伪造