GitHub Security Advisory: GHSA-m63r-m9jh-3vc6
Package: wwbn/avideo (COMPOSER)
Severity: MODERATE (CVSS 6.5)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: <= 29.0
Patched in: unpatched

Summary: WWBN AVideo has an Incomplete fix: Directory traversal bypass via query string in ReceiveImage downloadURL parameters

Details:
## Summary

The directory traversal fix introduced in commit 2375eb5e0 for `objects/aVideoEncoderReceiveImage.json.php` only checks the URL **path component** (via `parse_url($url, PHP_URL_PATH)`) for `..` sequences. However, the downstream function `try_get_contents_from_local()` in `objects/functionsFile.php` uses `explode('/videos/', $url)` on the **full URL string** including the query string. An attacker can place the `/videos/../../` traversal payload in the query string to bypass the security check and read arbitrary files from the server filesystem.

## Details

The security fix at commit 2375eb5e0 added a traversal check at `objects/aVideoEncoderReceiveImage.json.php:49`:

```php
$decodedPath = urldecode((string)(parse_url($_REQUEST[$value], PHP_URL_PATH) ?? ''));
if (strpos($decodedPath, '..') !== false) {
    unset($_REQUEST[$value]);
}
```

This only inspects the **path component** of the URL. For a URL like `http://TARGET/x?a=/videos/../../etc/passwd`, `parse_url()` returns `/x` as the path — no `..` is found.

The URL then passes through `isValidURL()` (`objects/functions.php:4203`) which accepts it because `FILTER_VALIDATE_URL` considers `..` in query strings valid per RFC 3986.

It also passes `isSSRFSafeURL()` (`objects/functions.php:4264`) because the host matches `webSiteRootURL`, causing an early return at line 4294.

The URL reaches `url_get_contents()` (`objects/functions.php:1938`) which calls `try_get_contents_from_local()` (`objects/functionsFile.php:214`):

```php
function try_get_contents_from_local($url)
{
    // ...
    $parts = explode('/videos/', $url);
    if (!empty($parts[1])) {
        // ...
        $tryFile = "{$global['systemRootPath']}{$encoder}videos/{$parts[1]}";
        if (file_exists($tryFile)) {
            return file_get_contents($tryFile);
        }
    }
    return false;
}
```

`explode('/videos/', $url)` operates on the **entire URL string** including the query string. For the malicious URL, `$parts[1]` becomes `../../../../../../etc/passwd`, constructing a path like `/var/www/html/AVideo/Encoder/videos/../../../../../../etc/passwd` which PHP's filesystem functions resolve to `/etc/passwd`.

The file content is returned to the caller and written to the video's thumbnail path via `_file_put_contents()`. All four `downloadURL_*` parameters (`downloadURL_image`, `downloadURL_gifimage`, `downloadURL_webpimage`, `downloadURL_spectrumimage`) are affected.

## PoC

Prerequisites: An authenticated AVideo user account with upload permission and an existing video they own (with known `videos_id`).

1. Identify the AVideo instance's domain (e.g., `https://avideo.example.com`).

2. Send a POST request to the ReceiveImage endpoint with the traversal payload in the query string:

```bash
curl -s -b "PHPSESSID=<session_cookie>" \
  "https://avideo.example.com/objects/aVideoEncoderReceiveImage.json.php" \
  -d "videos_id=<YOUR_VIDEO_ID>" \
  -d "downloadURL_image=http://avideo.example.com/x?a=/videos/../../../..