# A short story about an XSS in chat.mozilla.org (CVE-2021-21320)

Matrix usercontent iframe trusted a postMessage origin taken from the URL query string, letting an attacker inject an HTML blob and execute XSS on chat.mozilla.org.

- Payout: $500
- Severity: medium
- Program: Mozilla
- Credited: Guilherme Keerok
- Disclosed: 2021-03-19
- CVE: CVE-2021-21320
- Write-up quality: 7/10
- Original: https://gccybermonks.com/posts/xss-mozilla/
- Library page: https://bugbountylibrary.com/r/blog%3Agccybermonks-com-posts-xss-mozilla
- Tags: domain:web impact:ato kind:bounty platform:blog sev:medium tech:websocket technique:js-analysis technique:source-review vuln:xss

**Target:** usercontent iframe preview endpoint on chat.mozilla.org (matrix-react-sdk)
**Root cause:** The iframe's lockOrigin was parsed from the attacker-controllable `origin` query parameter, so any origin could satisfy the postMessage origin check; the received blob was then passed to createObjectURL without content-type validation.
**Steps:**
1. Open `https://chat.mozilla.org/usercontent/?origin=https%3a%2f%2f<attacker-domain>` in a new window
2. From the attacker page, `postMessage({blob: new Blob(['<script>alert(1337)</script>'], {type:'text/html'}), imgSrc: '<image>', auto: true}, '*')` to the opened window
3. The iframe sets `a.href = URL.createObjectURL(blob)` and `img.src = data.imgSrc`
4. Victim right-clicks the rendered image and opens it in a new tab (due to `a.download` attribute), executing the HTML blob as script on chat.mozilla.org origin
**How found:** While reviewing the open-source matrix-react-sdk, the researcher noticed the file-upload preview rendered inside an iframe and that the postMessage origin check read `lockOrigin` from `window.location.search`, i.e. user-controlled input.
**Exploitation:** Attacker hosts a page that opens the usercontent endpoint with a forged `origin` param matching the attacker's domain, then postMessages an HTML blob plus a lure image. The victim must right-click the image and open in a new tab, at which point the blob renders as HTML and executes JS in the chat.mozilla.org origin.
**Insight:** When an iframe validates postMessage origin, check whether the expected origin is derived from the URL/query rather than a hardcoded constant — a `?origin=` param that feeds the comparison defeats the check entirely. Also watch for createObjectURL on attacker-supplied blobs without content-type enforcement.
