# XSS в upload.php

upload.php reflected a user-controlled callback param into parent.<callback>(fn), and eval'd the XHR response; chaining requestAnimationFrame and an image-JS proxy yielded XSS on vk.com.

- Payout: $1,500
- Severity: high
- Program: VK.com
- Credited: irek
- Disclosed: 2017-02-09
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/142135
- Library page: https://bugbountylibrary.com/r/h1%3A142135
- Tags: domain:web impact:ato kind:bounty platform:hackerone sev:high tech:php technique:chained technique:js-analysis vuln:xss

**Target:** upload.php?act=transport on pu.vk.com
**Root cause:** The callback GET parameter is used as a property name on window.parent (parent.<callback>(...)), and sendData eval's the XHR response body; both attacker-influenced.
**Steps:**
1. Open `upload.php?act=transport&to_act=add_doc&hash=...&rhash=...&aid=1&mid=...&callback=getUploadSvg`
2. Note line 25 `eval` and line 62 `parent.getUploadSvg(function(data){sendData(url, data)})` where `getUploadSvg` comes from `callback`
3. Find a global function matching `/^[a-zA-Z0-9]*$/` that accepts a callback and invokes it with an object: `requestAnimationFrame`
4. Set `callback=requestAnimationFrame` so the anonymous fn is invoked with a timestamp, triggering `sendData`
5. Craft the URL passed to sendData so the POST response is attacker JS: use `share.php` image proxy + imagejs-embedded GIF
6. Obtain valid hashes by calling `upload_fails.php` with `role=share` and required params
7. Load the crafted URL in a vk.com app iframe; `eval` executes the proxied JS, `alert(document.cookie)`
**How found:** Noticed eval on line 25 and the callback param reflected into `parent.<callback>`; reasoned that a global function accepting a callback would trigger sendData; brute-forced `for (... in window)` to find requestAnimationFrame; then solved payload delivery via imagejs + share.php proxy and hash generation via upload_fails.php?role=share.
**Exploitation:** Attacker hosts a GIF with embedded JS (imagejs), proxies it via vk.com share.php image proxy to get same-origin response, crafts upload.php URL with valid hashes from upload_fails.php?role=share, sets callback=requestAnimationFrame so sendData fires, and the eval of the proxied image body executes JS in vk.com origin.
**Escalation:** Chained three primitives: callback-controlled parent function call, eval of XHR response, and same-origin image proxy to deliver JS payload.
**Insight:** When a callback name is used as a window property, enumerate `for (k in window)` for functions that accept and invoke a callback (requestAnimationFrame, setTimeout variants) to turn a name-reflection into code execution; pair with same-origin image proxies to serve JS from image files.
