Skip to content
Open
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 @@ -232,11 +232,50 @@ 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.

## References

- [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
- [https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
- 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)

{{#include ../../banners/hacktricks-training.md}}

Expand Down