# XSS in $shop$.myshopify.com/admin/ via twine template injection in "Shopify.API.Modal.input" method when using a malicious app

Malicious embedded Shopify app injects JS via unescaped value into lodash template's data-define attribute, which twine evals, yielding XSS as store admin.

- Payout: $1,000
- Severity: high
- Program: Shopify
- Credited: bored-engineer
- Disclosed: 2017-06-01
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/217790
- Library page: https://bugbountylibrary.com/r/h1%3A217790
- Tags: domain:web impact:ato kind:bounty platform:hackerone sev:high tech:nodejs technique:chained technique:js-analysis technique:source-review vuln:xss

**Target:** Embedded App SDK Shopify.API.Modal.input / twine data-define template
**Root cause:** lodash _.template escapes HTML but not the JSON/JS context of the data-define attribute; twine's wrapFunctionString passes the attribute value to new Function, turning injection into eval.
**Steps:**
1. Install malicious embedded app on $shop$.myshopify.com via /admin/oauth/authorize with any scope
2. App posts `{message:"Shopify.API.Modal.input",data:{message:{message:"",value:"'-alert(document.domain)-'"}}}` to parent
3. Modal renders data-define `{typedInput:''-alert(document.domain)-''}`
4. Twine wraps value in `with($context){with($registry){return {typedInput:''-alert(document.domain)-''}}}` and new Function executes it
5. Alert fires in /admin parent context
**How found:** Noticed the input template assigns value into a data-define attribute; tested that a single quote breaks the JSON (`{typedInput: 'some'value'}`), then read twine source and found wrapFunctionString uses new Function, confirming the injection is an eval primitive.
**Exploitation:** Attacker publishes an embedded app, convinces a store admin to authorize it (no special scopes needed), then the app sends a postMessage to the parent admin frame invoking Shopify.API.Modal.input with a crafted value; twine evaluates the injected expression in the admin origin, executing arbitrary JS as the administrator.
**Insight:** When a template engine escapes for HTML but the value lands in a JS/JSON attribute consumed by a client-side binding library, read the binding library's evaluator (here new Function) to confirm injection becomes code execution rather than just broken markup.
