◆ postMessage XSS lab origin …
postMessage XSS lab

Scenarios

Every row is one scenario, and every scenario is two pages: the page of the application under test, and the page you drive it from. The two sit on different origins on purpose — a postMessage handler that trusts "*" (or a sloppy origin check) has to be reachable from somewhere else for the exercise to mean anything.

Every participant runs on its own real origin — a separate hostname under pmlab.amir.baby, all served by this one server. Origin checks are genuine and a blocked message really is blocked.
The attacker page takes the payload from its own URL (?payload=…), so a whole scenario can be run from one crafted link — send it through Burp, or just open it.
#FindingApplication under testDrive it from
S1Wildcard target origin
The listener accepts every sender and writes the payload into the DOM as HTML.
https://victim.pmlab.amir.baby/s1https://attacker.pmlab.amir.baby/s1
S2Weak domain validation
The origin check looks for the trusted domain inside event.origin, so a subdomain of it passes.
https://victim.pmlab.amir.baby/s2https://attacker.trusted-domain.pmlab.amir.baby/s2
S3Weak regex in the origin check
The origin is matched with a regular expression whose .* swallows extra labels.
https://victim.pmlab.amir.baby/s3https://attacker.com.trusted-origin.pmlab.amir.baby/s3
S4Data exposure with a wildcard target origin
The session context is sent with "*" as the target origin, so whoever opened or framed the page receives it.
https://victim.pmlab.amir.baby/s4https://attacker.pmlab.amir.baby/s4
S5The hardened listener
The same flow with an origin allowlist, a source check and a text-only sink.
https://victim.pmlab.amir.baby/s5https://attacker.pmlab.amir.baby/s5

How the flow works

Sender Window                                    Receiver Window
──────────────                                   ───────────────
const receiverURL = "http://localhost/receiver.html";
const childWindow = window.open(receiverURL);      window.addEventListener('message', (event) => {
                                                     document.querySelector('p').textContent = event.data;
function sendMessage() {                           });
  const message = "Hello, Receiver!";
  childWindow.postMessage(message, "*");

The receiver decides how much it trusts the sender. Everything in this lab is a variation of that one line — what the receiver checks, and what it does with the payload when it believes the sender.