# Stored XSS in the guide's GameplayVersion (www.dota2.com)

Attacker moved an XSS payload from a sanitized guide title into the unsanitized GameplayVersion field via a proxy, then published the build to get stored XSS on dota2.com.

- Payout: $750
- Severity: medium
- Program: Valve
- Credited: mvc
- Disclosed: 2019-01-07
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/380045
- Library page: https://bugbountylibrary.com/r/h1%3A380045
- Tags: domain:web impact:ato kind:bounty platform:hackerone sev:medium tech:payment technique:incomplete-fix technique:source-review vuln:xss

**Target:** Dota 2 workshop guide build publishing (www.dota2.com/workshop/builds/view)
**Root cause:** The GameplayVersion field of a published guide build is rendered into the public workshop page without HTML sanitization, unlike the guide title which is sanitized.
**Steps:**
1. Create a Dota 2 guide with XSS payload in the title and publish it
2. Intercept the client's PUT request to /cloud/CB/ in Fiddler
3. Rewrite body: move `mvc123<svg/onload=alert(document.domain)>` from title to GameplayVersion, keeping byte length identical to pass hash check
4. Edit and republish the build from the client so the modified PUT succeeds
5. Read the public FileID from Dota 2 Workshop Manager
6. Visit `http://www.dota2.com/workshop/builds/view?fileid=<id>` to trigger stored XSS
**How found:** Prior report showed the title field was sanitized; researcher hypothesized other build fields might not be, and used a length-preserving payload swap to test GameplayVersion while defeating the hash check.
**Exploitation:** Attacker publishes a guide, intercepts the client's upload request, swaps the payload into GameplayVersion while preserving content length to bypass the hash comparison, republishes, and shares the public build URL so any visitor executes the script in dota2.com origin.
**Insight:** When a client uploads structured data with an integrity hash, look for sibling fields that share the same renderer but lack sanitization; a length-preserving payload swap lets you move a blocked payload into an unsanitized field without breaking the hash.
