Skip to content
Merged
Show file tree
Hide file tree
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
17 changes: 14 additions & 3 deletions src/pentesting-web/oauth-to-account-takeover.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,13 @@ Connection: close
code=77515&redirect_uri=http%3A%2F%2F10.10.10.10%3A3000%2Fcallback&grant_type=authorization_code&client_id=public_client_id&client_secret=[bruteforce]
```

### Referer Header leaking Code + State
### Referer/Header/Location artifacts leaking Code + State

Once the client has the **code and state**, if they surface in **`location.href`** or **`document.referrer`** and are forwarded to third parties, they leak. Two recurring patterns:

- **Classic Referer leak**: after the OAuth redirect, any navigation that keeps `?code=&state=` in the URL will push them into the **Referer** header sent to CDNs/analytics/ads.
- **Telemetry/analytics confused deputy**: some SDKs (pixels/JS loggers) react to `postMessage` events and then **send the current `location.href`/`referrer` to backend APIs using a token supplied in the message**. If you can inject your own token into that flow (e.g., via an attacker-controlled postMessage relay), you can later read the SDK’s API request history/logs and recover the victim’s OAuth artifacts embedded in those requests.

Once the client has the **code and state**, if it's **reflected inside the Referer header** when he browses to a different page, then it's vulnerable.

### Access Token Stored in Browser History

Expand All @@ -167,7 +171,13 @@ Combining a replay-friendly code with any `redirect_uri` or logging bug allows p

### Authorization/Refresh Token not bound to client

If you can get the **authorization code and use it with a different client then you can takeover other accounts**.
If you can get the **authorization code** and **redeem it for a different client/app**, you can takeover other accounts. Test for weak binding by:

- Capturing a `code` for **app A** and sending it to **app B’s token endpoint**; if you still receive a token, audience binding is broken.
- Trying first-party token minting endpoints that should be restricted to their own client IDs; if they accept arbitrary `state`/`app_id` while only validating the code, you effectively perform an **authorization-code swap** to mint higher-privileged first-party tokens.
- Checking whether client binding ignores nonce/redirect URI mismatches. If an error page still loads SDKs that log `location.href`, combine with Referer/telemetry leaks to steal codes and redeem them elsewhere.

Any endpoint that exchanges `code` → token **must** verify the issuing client, redirect URI, and nonce; otherwise, a stolen code from any app can be upgraded to a first-party access token.

### Happy Paths, XSS, Iframes & Post Messages to leak code & state values

Expand Down Expand Up @@ -332,5 +342,6 @@ In mobile OAuth implementations, apps use **custom URI schemes** to receive redi
- [**https://blog.doyensec.com/2025/01/30/oauth-common-vulnerabilities.html**](https://blog.doyensec.com/2025/01/30/oauth-common-vulnerabilities.html)
- [An Offensive Guide to the OAuth 2.0 Authorization Code Grant](https://www.nccgroup.com/research-blog/an-offensive-guide-to-the-authorization-code-grant/)
- [OAuth Discovery as an RCE Vector (Amla Labs)](https://amlalabs.com/blog/oauth-cve-2025-6514/)
- [Leaking fbevents: OAuth code exfiltration via postMessage trust leading to Instagram ATO](https://ysamm.com/uncategorized/2026/01/16/leaking-fbevents-ato.html)

{{#include ../banners/hacktricks-training.md}}
16 changes: 16 additions & 0 deletions src/pentesting-web/postmessage-vulnerabilities/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,21 @@ In order to **find event listeners** in the current page you can:

- The `document.domain` property in JavaScript can be set by a script to shorten the domain, allowing for more relaxed same-origin policy enforcement within the same parent domain.

### Origin-only trust + trusted relays

If a receiver only checks **`event.origin`** (e.g., trusts any `*.trusted.com`) you can often find a **"relay" page on that origin that echoes attacker-controlled params via `postMessage`** to a supplied `targetOrigin`/`targetWindow`. Examples include marketing/analytics gadgets that take query params and forward `{msg_type, access_token, ...}` to `opener`/`parent`. You can:

- **Open the victim page in a popup/iframe that has an `opener`** so its handlers register (many pixels/SDKs only attach listeners when `window.opener` exists).
- **Navigate another attacker window to the relay endpoint on the trusted origin**, populating message fields you want injected (message type, tokens, nonces).
- Because the message now comes **from the trusted origin**, origin-only validation passes and you can trigger privileged behaviors (state changes, API calls, DOM writes) in the victim listener.

Abuse patterns seen in the wild:

- Analytics SDKs (e.g., pixel/fbevents-style) consume messages like `FACEBOOK_IWL_BOOTSTRAP`, then **call backend APIs using a token supplied in the message** and include **`location.href` / `document.referrer`** in the request body. If you supply your own token, you can **read these requests in the token’s request history/logs** and exfil **OAuth codes/tokens** present in the URL/referrer of the victim page.
- Any relay that reflects arbitrary fields into `postMessage` lets you **spoof message types** expected by privileged listeners. Combine with weak input validation to reach Graph/REST calls, feature unlocks, or CSRF-equivalent flows.

Hunting tips: enumerate `postMessage` listeners that only check `event.origin`, then look for **same-origin HTML/JS endpoints that forward URL params via `postMessage`** (marketing previews, login popups, OAuth error pages). Stitch both together with `window.open()` + `postMessage` to bypass origin checks.

### e.origin == window.origin bypass

When embedding a web page within a **sandboxed iframe** using %%%%%%, it's crucial to understand that the iframe's origin will be set to null. This is particularly important when dealing with **sandbox attributes** and their implications on security and functionality.
Expand Down Expand Up @@ -236,6 +251,7 @@ For **more information**:

- [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)
- [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)

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