# H1514 DOMXSS on Embedded SDK via Shopify.API.setWindowLocation abusing cookie Stuffing

Embedded App SDK's setWindowLocation accepted javascript: URLs; chained cookie stuffing, login CSRF and redirect to run XSS in victim's embedded-app session.

- Payout: $2,500
- Severity: high
- Program: Shopify
- Found by: filedescriptor
- Disclosed: 2019-04-17
- Write-up quality: 9/10
- Original: https://hackerone.com/reports/422043
- Library page: https://bugbountylibrary.com/r/h1%3A422043
- Tags: area:web domain:web impact:ato kind:bounty platform:hackerone sev:high technique:chained vuln:csrf vuln:open-redirect vuln:xss

**Target:** Embedded App SDK postMessage handler (Shopify.API.setWindowLocation)
**Root cause:** The SDK's setWindowLocation handler assigns the postMessage data directly to window.location without validating the URL scheme, so a javascript: pseudo-URL executes script. Origin validation only checks the message comes from the logged-in shop, which an attacker controls on their own store.
**Steps:**
1. Be logged in as admin on your store with an embedded app (e.g. Script Editor) installed.
2. Open your store page and post to the embedded app iframe: `$$('iframe')[0].contentWindow.postMessage('{"message":"Shopify.API.setWindowLocation","data":"javascript:alert(document.domain);0[0]"}','*')`
3. Observe the javascript: URL executes in the embedded app origin.
4. To target a victim: stuff cookies with `document.cookie = '_secure_admin_session_id=EVIL;path=/admin/oauth';` and `document.cookie = '_master_udr=EVIL;path=/admin/oauth';` so the longer path wins per RFC 6265.
5. Navigate victim to `/admin/oauth/authorize` to log them into attacker's store, then to `https://www.shopify.com/admin/oauth/authorize` to redirect them to their own store's `/admin/oauth/authorize` and re-auth the embedded app session.
6. Deliver the postMessage payload to run XSS with the victim's embedded-app session.
**How found:** Inspired by prior report #381192, audited origin validation across pages; noticed embedded apps validate origin against the logged-in shop (shopOrigin), meaning attacker-controlled stores pass validation. Beautified the SDK source and grepped common DOMXSS sinks, finding setWindowLocation assigning postMessage data to window.location.
**Exploitation:** Attacker hosts a page on their own shop that iframes a victim-used embedded app, stuffs session cookies scoped to /admin/oauth to force login CSRF into the attacker's store, then redirects the victim through shopify.com/admin/oauth/authorize to re-establish the victim's own store session in the embedded app, and finally posts the javascript: setWindowLocation message to execute script in the app origin with the victim's session, performing unauthorized actions on their store.
**Escalation:** Chained cookie stuffing (path-scoped cookie shadowing) + login CSRF + open redirect via shopify.com to convert a self-XSS into a cross-victim XSS on any embedded app.
**Insight:** When an SDK validates postMessage origin against a value the attacker can control (their own tenant/shop), the origin check is meaningless—hunt for sinks like window.location assignment that accept javascript: URLs. Also, path-scoped cookies can shadow domain-scoped ones per RFC 6265 to win cookie-stuffing races.

## Proof of concept (copied exactly from the original report)

**postMessage payload triggering setWindowLocation DOMXSS**

```
$$('iframe')[0].contentWindow.postMessage('{"message":"Shopify.API.setWindowLocation","data":"javascript:alert(document.domain);0[0]"}','*')
```

**Cookie stuffing with path override**

```
document.cookie = '_secure_admin_session_id=EVIL;path=/admin/oauth';
document.cookie = '_master_udr=EVIL;path=/admin/oauth';
```

**OAuth authorize request with stuffed cookies**

```
GET https://canvasfoobar.myshopify.com/admin/oauth/authorize?client_id=d25e45407e508f96409c2dd796e9bd95&redirect_uri=https%3A%2F%2Fscript-editor.shopifycloud.com%2Fauth%2Fshopify%2Fcallback&response_type=code&scope=write_scripts%2Cread_products%2Cread_customers&state=a HTTP/1.1
Host: canvasfoobar.myshopify.com
Cookie: _master_udr=LEGIT; _master_udr=EVIL; _secure_admin_session_id=EVIL
```

