Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/pentesting-web/postmessage-vulnerabilities/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,44 @@ For **more information**:
- Link to page about [**XSS**](../xss-cross-site-scripting/index.html)
- Link to page about [**client side prototype pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)

### Origin-derived script loading & supply-chain pivot (CAPIG case study)

`capig-events.js` only registered a `message` handler when `window.opener` existed. On `IWL_BOOTSTRAP` it checked `pixel_id` but stored `event.origin` and later used it to build `${host}/sdk/${pixel_id}/iwl.js`.

<details>
<summary>Handler writing attacker-controlled origin</summary>

```javascript
if (window.opener) {
window.addEventListener("message", (event) => {
if (
!localStorage.getItem("AHP_IWL_CONFIG_STORAGE_KEY") &&
!localStorage.getItem("FACEBOOK_IWL_CONFIG_STORAGE_KEY") &&
event.data.msg_type === "IWL_BOOTSTRAP" &&
checkInList(g.pixels, event.data.pixel_id) !== -1
) {
localStorage.setItem("AHP_IWL_CONFIG_STORAGE_KEY", {
pixelID: event.data.pixel_id,
host: event.origin,
sessionStartTime: event.data.session_start_time,
})
startIWL() // loads `${host}/sdk/${pixel_id}/iwl.js`
}
})
}
```

</details>

**Exploit (origin → script-src pivot):**
1. Get an opener: e.g., in Facebook Android WebView reuse `window.name` with `window.open(target, name)` so the window becomes its own opener, then post a message from a malicious iframe.
2. Send `IWL_BOOTSTRAP` from any origin to persist `host = event.origin` in `localStorage`.
3. Host `/sdk/<pixel_id>/iwl.js` on any CSP-allowed origin (takeover/XSS/upload on a whitelisted analytics domain). `startIWL()` then loads attacker JS in the embedding site (e.g., `www.meta.com`), enabling credentialed cross-origin calls and account takeover.

If direct opener control was impossible, compromising a third-party iframe on the page still allowed sending the crafted `postMessage` to the parent to poison the stored host and force the script load.

**Backend-generated shared script → stored XSS:** the plugin `AHPixelIWLParametersPlugin` concatenated user rule parameters into JS appended to `capig-events.js` (e.g., `cbq.config.set(...)`). Injecting breakouts like `"]}` injected arbitrary JS, creating stored XSS in the shared script served to all sites loading it.

### Trusted-origin allowlist isn't a boundary

A strict `event.origin` check only works if the **trusted origin cannot run attacker JS**. When privileged pages embed third-party iframes and assume `event.origin === "https://partner.com"` is safe, any XSS in `partner.com` becomes a bridge into the parent:
Expand Down Expand Up @@ -314,6 +352,7 @@ iframe.location = fbMsg // sends postMessage from facebook.com with forged callb
- [https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
- [Leaking fbevents: OAuth code exfiltration via postMessage trust leading to Instagram ATO](https://ysamm.com/uncategorized/2026/01/16/leaking-fbevents-ato.html)
- To practice: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
- [CAPIG postMessage origin trust → script loading + stored JS injection](https://ysamm.com/uncategorized/2025/01/13/capig-xss.html)
- [Self XSS Facebook Payments](https://ysamm.com/uncategorized/2026/01/15/self-xss-facebook-payments.html)
- [Facebook JavaScript SDK Math.random callback prediction → DOM XSS writeup](https://ysamm.com/uncategorized/2026/01/17/math-random-facebook-sdk.html)
- [V8 Math.random() state recovery (Z3 predictor)](https://github.com/PwnFunction/v8-randomness-predictor)
Expand Down