Lion Cipher Lab
Exploit and fix a cross-origin postMessage flaw (web messaging) in your own lab
- AppSec / DevSecOps
- Intermediate
- ~50 min
- Free · no account required
Why it matters
`window.postMessage` is how a page talks to an iframe or pop-up on another origin — payment widgets, chat bubbles, SSO buttons, analytics beacons and third-party embeds all lean on it. The Same-Origin Policy deliberately does NOT block these messages, which means the code that receives them is the entire trust boundary. A listener that acts on every message it gets — and worse, drops the payload into `innerHTML` — is a cross-origin hole an attacker triggers just by loading your widget in a frame they control. This is a real, common finding (its own OWASP WSTG test, WSTG-CLNT-11, and CWE-346,…
What you'll learn
- how window.postMessage crosses origins and why the receiver is the trust boundary
- exploiting a message listener that never validates event.origin (CWE-346)
- turning an unchecked message into DOM-based XSS in a trusted origin
- validating event.origin against an allowlist (not startsWith/includes)
- validating message shape and using a safe DOM sink (textContent, not innerHTML)
- sending with an explicit targetOrigin instead of "*"
- finding these bugs in your own code (grep for addEventListener('message'))
Steps in this lab
- What web messaging is, and where the trust boundary really sits
- Make two origins on one machine (no hosts-file editing needed)
- Build the vulnerable widget (the receiver that trusts everyone)
- Build the legitimate app that embeds the widget
- Start the trusted server and confirm the widget works
- Build the attacker page on the second origin
- Fire the attack from a second server and watch it pop
- Name the root cause precisely (CWE-346 + an HTML sink)
- Fix #1 — check event.origin against an exact allowlist
- Fix #2 — validate the message shape and use a safe sink
- Fix #3 — send with an explicit targetOrigin, never "*"
- Re-run the attack and prove it's dead
- The edge cases that bite people
- Find these in your own code
- What you did — and the rule to carry forward