Snappy: Binary path is never shell-escaped due to an inverted is_executable check
Description
Impact
On POSIX, escapeshellarg(‘/usr/bin/wkhtmltopdf’) returns the literal string ‘/usr/bin/wkhtmltopdf’ with the single-quote characters included. is_executable() then looks for a file whose actual name contains those quote characters, which essentially never exists. The safe branch is dead code and $command always falls through to the raw, unescaped value.
The rest of the arguments (options, input, output) are escaped correctly, so injection has to land in the binary string itself. That happens whenever the binary path is sourced from configuration that is user-influenced, derived from environment variables that ultimately come from request data, or concatenated with any user-controlled fragment.
Proof of concept:
$pdf = new Knp\Snappy\Pdf(‘wkhtmltopdf; touch /tmp/snappy_rce’);
$pdf->generate(‘https://example.com’, ‘/tmp/out.pdf’);
// /tmp/snappy_rce is created.
Impact: command execution as the PHP process when the binary path is attacker-influenced. Even in deployments where the binary is hard-coded, this is a defensive-in-depth regression: downstream packages reasonably assume Snappy shell-escapes the binary because the code looks like it does.
Patches
The version 1.7.1 will resolve this security advisory.
Workarounds
Before calling the constructor, ensure \is_executable($path) is truthy.
// Bad example
$pdf = new Knp\Snappy\Pdf('/path/to/binary');
// Better example
$pathToBinary = '/path/to/binary';
if (!\is_executable($pathToBinary)) {
throw new \RuntimeException();
}
$pdf = new Knp\Snappy\Pdf('/path/to/binary');
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Snappy for PHP, an inverted is_executable() check causes the binary path to never be shell-escaped, enabling remote command injection when the path is attacker-influenced.
The Snappy PHP library (versions ≤1.7.0) contains a command injection vulnerability in its binary path handling on POSIX systems. The function escapeshellarg() is called on the binary path, but the result—including the added single-quote characters—is then passed to is_executable(). This check looks for a file whose name literally contains those single quotes, which never exists, causing the check to fail silently and the safe branch to become dead code. Consequently, the raw unescaped binary path is used in the shell command [1].
An attacker who can influence the binary path—either directly through the constructor argument, or indirectly via configuration or environment variables derived from user input—can inject arbitrary shell commands. The rest of the arguments (options, input, output) are correctly escaped, so injection must be placed in the binary string itself. As demonstrated in the proof of concept, passing a path like wkhtmltopdf; touch /tmp/snappy_rce results in execution of the injected command [2].
When exploitable, this vulnerability allows an attacker to achieve arbitrary command execution as the PHP process. Even in deployments where the binary path is hard-coded, the vulnerability represents a defensive-in-depth regression, because downstream packages reasonably assume that Snappy escapes the binary path given that the code appears to do so [1].
The maintainers have released version 1.7.1 to fix the issue. Until that version is applied, users can mitigate the risk by explicitly verifying the binary path with \is_executable() before passing it to the Snappy constructor, and throwing an exception if the check fails [1][2].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
0No patches discovered yet.
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
2News mentions
2- Patch Tuesday - May 2026Rapid7 Blog · May 13, 2026
- Microsoft tests modern Windows Run, says it's faster than legacy dialogBleepingComputer · May 2, 2026