Responsible-disclosure draft — AVideo
Title: Incomplete fix for CVE-2026-45578: execAsync() re-wrapping in sh -c "…"
re-enables OS command injection via $()/backtick despite escapeshellarg()
Summary
The remediation for CVE-2026-45578 (GHSA-xw67-cg5f-4m2r) replaced unsafe
single-quote interpolation in plugin/Live/on_publish.php with escapeshellarg()
per token — the primitive the advisory recommended and that the project already uses
in ~137 places. However, the patched code forwards the assembled command to the shared
helper execAsync() (objects/functionsExec.php), which re-embeds the whole string
inside an outer double-quoted sh -c:
$command = addcslashes($command, '"'); // escapes only "
$cmd = "nohup sh -c \"$command & echo \\$!\" > /dev/null 2>&1 &";
exec($cmd);
Inside those double quotes, $( … ) command substitution and backticks remain active
even when the value is wrapped in escapeshellarg()'s single quotes, and
addcslashes($command,'"') does not neutralise $ or `. A value carrying a
$( … ) payload therefore executes arbitrary commands — same sink, same impact as the
original CVE.
This affects every execAsync() caller that forwards attacker-influenced data
protected only by escapeshellarg(). The standalone FFmpeg path
(sanitizeFFmpegCommand()) is the lone predecessor that strips $()/`, and is
not affected.
Proof (benign)
Running the unmodified upstream execAsync() with a command built exactly as patched
on_publish.php does, where one escapeshellarg()-quoted token is
$(touch /tmp/SENTINEL), creates the sentinel file — confirming command substitution.
A control using sanitizeFFmpegCommand() (which strips $()) does not, and a benign
value does not. No destructive action is performed; the sentinel is an empty marker file.
Remediation
Fix the helper, not each call site:
- have
execAsync() execute via an argv array without a shell
(proc_open array form / Symfony Process array), eliminating shell re-parsing; or
- if a shell is required,
escapeshellarg() the complete assembled sh -c payload at the
helper boundary and remove the addcslashes($command,'"')-only escaping.
Credit: measurement study of incomplete security fixes (responsible disclosure).
Responsible-disclosure draft — AVideo
Title: Incomplete fix for CVE-2026-45578:
execAsync()re-wrapping insh -c "…"re-enables OS command injection via
$()/backtick despiteescapeshellarg()Summary
The remediation for CVE-2026-45578 (GHSA-xw67-cg5f-4m2r) replaced unsafe
single-quote interpolation in
plugin/Live/on_publish.phpwithescapeshellarg()per token — the primitive the advisory recommended and that the project already uses
in ~137 places. However, the patched code forwards the assembled command to the shared
helper
execAsync()(objects/functionsExec.php), which re-embeds the whole stringinside an outer double-quoted
sh -c:Inside those double quotes,
$( … )command substitution and backticks remain activeeven when the value is wrapped in
escapeshellarg()'s single quotes, andaddcslashes($command,'"')does not neutralise$or`. A value carrying a$( … )payload therefore executes arbitrary commands — same sink, same impact as theoriginal CVE.
This affects every
execAsync()caller that forwards attacker-influenced dataprotected only by
escapeshellarg(). The standalone FFmpeg path(
sanitizeFFmpegCommand()) is the lone predecessor that strips$()/`, and isnot affected.
Proof (benign)
Running the unmodified upstream
execAsync()with a command built exactly as patchedon_publish.phpdoes, where oneescapeshellarg()-quoted token is$(touch /tmp/SENTINEL), creates the sentinel file — confirming command substitution.A control using
sanitizeFFmpegCommand()(which strips$()) does not, and a benignvalue does not. No destructive action is performed; the sentinel is an empty marker file.
Remediation
Fix the helper, not each call site:
execAsync()execute via an argv array without a shell(
proc_openarray form / SymfonyProcessarray), eliminating shell re-parsing; orescapeshellarg()the complete assembledsh -cpayload at thehelper boundary and remove the
addcslashes($command,'"')-only escaping.Credit: measurement study of incomplete security fixes (responsible disclosure).