Skip to content

** Incomplete fix for CVE-2026-45578: `execAsync()` re-wrapping in `sh -c "…"`

High
DanielnetoDotCom published GHSA-rc5x-vh5v-473f Jul 7, 2026

Package

Avideo

Affected versions

<=v29.0

Patched versions

>= 29.0

Description

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).

Severity

High

CVE ID

No known CVE

Weaknesses

No CWEs

Credits