# Tale of a Wormable Twitter XSS

Stored XSS in Twitter welcome-message deeplink card text, bypassing tag stripping and CSP via JSONP on twimg.com, enabling a self-retweeting worm.

- Payout: $2,940
- Severity: high
- Program: Twitter
- Credited: Ahmed Elsobky
- Disclosed: 2019-05-02
- Write-up quality: 9/10
- Original: https://www.virtuesecurity.com/tale-of-a-wormable-twitter-xss/
- Library page: https://bugbountylibrary.com/r/blog%3Avirtuesecurity-com-tale-of-a-wormable-twitter-xss
- Tags: domain:web impact:ato impact:integrity kind:bounty platform:blog sev:high tech:oauth technique:chained technique:parser-differential technique:source-review vuln:auth-bypass vuln:xss

**Target:** Twitter card iframe / welcome_message_id deeplink text parameter
**Root cause:** The default_composer_text value was reflected into an inline JSON script block without escaping </script>, and the tag-stripping filter could be bypassed with nested <</<x>/script/...> constructs. CSP allowed *.twimg.com, which hosted a JSONP endpoint with a lax callback validator.
**Steps:**
1. Craft payload `</script><svg onload=alert()>` and observe tag stripping turns it into `ab`-style output.
2. Fuzz nesting to get `<</<x>/script/test000><</</x><x>svg onload=alert()></><script>1<\x>2` which normalizes to `</script/test000><svg onload=alert()>`.
3. Deliver via `https://twitter.com/messages/compose?recipient_id=...&welcome_message_id=...&text=<payload>` so the card iframe reflects it into `default_composer_text`.
4. Bypass CSP by loading `//syndication.twimg.com/timeline/profile?callback=__twttr/alert;user_id=12` (whitelisted *.twimg.com JSONP, forward slash allowed in callback).
5. Define `window.__twttr` by setting an iframe `id=__twttr` so `__twttr/alert` resolves without ReferenceError.
6. Use `__twttr/frames[0].retweet_btn_form.submit` to auto-retweet, and chain tweets to spread the worm.
**How found:** Noticed HTML-tag stripping (rather than escaping) is parser-based and error-prone, so fuzzed nested tag constructs until the stripper produced a valid `</script>` breakout; then reviewed CSP, spotted permissive `*.twimg.com` wildcard, and hunted a JSONP endpoint with weak callback validation.
**Exploitation:** Victim loads a tweet containing the crafted welcome-message deeplink; the same-origin card iframe reflects the payload, breaking out of the JSON script block. JSONP on syndication.twimg.com executes attacker-chosen function calls (alert to synchronize, then form submit) to retweet the payload tweet and follow the attacker. Staged payloads can also load /oauth/authorize in an iframe and submit oauth_form to silently grant a malicious third-party app full account access.
**Escalation:** Chained XSS to CSP bypass via JSONP, then to worm propagation via retweet intents, and further to silent OAuth app authorization granting full account takeover.
**Insight:** When a filter strips tags instead of escaping, fuzz nested/partial tag syntax to find normalization mismatches; and when CSP whitelists a wildcard subdomain, hunt JSONP endpoints there whose callback validator allows slashes or property access to reach arbitrary functions.
