CVE-2026-60102: Verified Reproduction
CVE-2026-60102: Horde VFS < 3.0.1 OS command injection via Horde Vfs Smb driver
CVE-2026-60102 is verified against horde/vfs · github. Affected versions: < 3.0.1. Fixed in 3.0.1. Vulnerability class: Command Injection. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00288.
What Is CVE-2026-60102?
CVE-2026-60102 is a high-severity OS command injection in the Horde VFS API (Horde_Vfs_Smb driver) before 3.0.1. Authenticated users can inject shell commands through crafted filenames used in normal VFS operations. Pruva reproduced it (reproduction REPRO-2026-00288).
CVE-2026-60102 Severity & CVSS Score
CVE-2026-60102 is rated high severity, with a CVSS base score of 8.8 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected horde/vfs Versions
horde/vfs · github versions < 3.0.1 are affected.
How to Reproduce CVE-2026-60102
pruva-verify REPRO-2026-00288 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00288/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-60102
- reached the target end-to-end
- high confidence
- the upstream fix blocks the same trigger
folder/file name supplied to authenticated VFS operations
- Horde_Vfs_Smb::createFolder()
- _command()
- _execute()
- proc_open()
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for CVE-2026-60102
Horde VFS before 3.0.1 builds a shell command string in Horde_Vfs_Smb::_command() and executes it with proc_open(). The _escapeShellCommand() helper only escapes semicolons and backslashes, leaving command substitution ($(...)) and backticks intact. A crafted file or folder name containing $(...) is therefore interpolated into a double-quoted shell context and executed by /bin/sh -c before the configured smbclient binary is ever invoked. We confirmed this by calling the real Horde_Vfs_Smb::createFolder() method with a folder name of $(id > /tmp/horde_vfs_smb_poc_folder).dir; the vulnerable commit creates the marker file with id output, while the fixed commit leaves the marker untouched and passes the literal payload as an argument to smbclient.
- Package/component: Horde VFS, specifically the
Horde_Vfs_Smbdriver (lib/Horde/Vfs/Smb.php). - Affected versions: All versions before commit
41f74b4acfc144e09013d04dd121e0a5da808361(released asv3.0.1). - Risk level: High. An authenticated caller can inject arbitrary shell commands through normal VFS operations such as file upload, create folder, rename, delete, or read paths.
- Consequences: Full OS command execution as the user running the PHP process.
Impact Parity
- Disclosed/claimed maximum impact: Code execution (OS command injection) via
proc_open()//bin/sh -c. - Reproduced impact from this run: We demonstrated arbitrary command execution by causing the shell to run
id > /tmp/horde_vfs_smb_poc_folderand captured the resultinguid/gidoutput. - Parity:
fullfor thecreateFolder()path; the same quoting weakness is present in all VFS operations that build thesmbclient -ccommand string. - Not demonstrated: No remote authentication bypass was needed; the reproduction assumes an already authenticated VFS caller as described in the CVE.
Root Cause
In the vulnerable code (lib/Horde/Vfs/Smb.php before 41f74b4^), _command() concatenates user-supplied paths/names into a single string:
$fullcmd = $this->_params['smbclient']
. ' "//' . $this->_params['hostspec'] . '/' . $share . '"'
. ' "-U' . $this->_params['username'] . '"'
. ' -D "' . $path . '"'
. ' -c "';
foreach ($cmd as $c) {
$fullcmd .= $c . ";";
}
$fullcmd .= '"';
return $this->_execute($fullcmd);
_execute() passes the string directly to proc_open(), which invokes the configured shell (/bin/sh -c) when the command is not an array. The _escapeShellCommand() helper only escapes ; and \, so $, backticks, parentheses, and quotes remain active. A folder name like $(id > /tmp/horde_vfs_smb_poc_folder).dir is first quoted in a double-quoted mkdir "..." segment, but the shell still expands $(...) before running smbclient.
The fix (41f74b4acfc144e09013d04dd121e0a5da808361) replaces the shell string with an argv array passed to proc_open() and adds _quoteSmbArg() to escape single and double quotes inside the smbclient -c mini-language. Because proc_open() with an array calls execvp() directly, no shell is involved and shell metacharacters lose their special meaning.
Reproduction Steps
- Run
bundle/repro/reproduction_steps.shfrom the bundle directory. - The script checks out the vulnerable commit (
41f74b4^) and the fixed commit (41f74b4) ofhorde/Vfsinto a cached repository. - It installs PHP and Composer, then runs
composer installfor each commit. - It creates a fake
smbclientthat logs arguments and exits 0. - It runs a PHP harness that instantiates
Horde_Vfs_Smbwith the fakesmbclientand callscreateFolder('', '$(id > /tmp/horde_vfs_smb_poc_folder).dir'). - Expected evidence: on the vulnerable commit,
/tmp/horde_vfs_smb_poc_folderis created and contains theidoutput; on the fixed commit, the marker is not created and the fakesmbclientreceives the literal payload.
Evidence
bundle/logs/reproduction_steps.log— overall run summary, commit SHAs, and captured fakesmbclientarguments.bundle/logs/vulnerable.log— showsMarker exists: YESwithuid=1000(vscode) ....bundle/logs/fixed.log— showsMarker exists: NO.bundle/logs/vulnerable_smbclient.log— the fakesmbclientreceivesmkdir ".dir";because the shell already executed$(id > ...)and substituted an empty string.bundle/logs/fixed_smbclient.log— the fakesmbclientreceives the literalmkdir "$(id > /tmp/horde_vfs_smb_poc_folder).dir";, confirming no shell expansion occurred.bundle/repro/artifacts/fake_smbclient.sh— the fakesmbclientused as the command target.bundle/repro/artifacts/harness.php— the PHP harness that drives the real library path.
Key excerpts:
=== vulnerable (994f4a46fd76ea44eae2fe839216bb273ce49080) ===
Marker exists: YES
Marker content: uid=1000(vscode) gid=1000(vscode) groups=1000(vscode),969(969)
=== fixed (41f74b4acfc144e09013d04dd121e0a5da808361) ===
Marker exists: NO
ARG //127.0.0.1/share
ARG -c
ARG mkdir "$(id > /tmp/horde_vfs_smb_poc_folder).dir";
Recommendations / Next Steps
- Upgrade guidance: Upgrade to Horde VFS
v3.0.1or any release containing commit41f74b4acfc144e09013d04dd121e0a5da808361. - Fix approach: Avoid shell-string execution; pass the
smbclientcommand as anargvarray toproc_open()and quote names inside thesmbclient -cmini-language rather than relying on shell quoting. - Testing recommendations: Add unit tests for
createFolder,write,rename,delete, andreadFilewith payloads containing$(...), backticks, pipes, semicolons, and quotes to prevent regression.
Additional Notes
- Idempotency: The reproduction script was run twice consecutively with identical results; both the vulnerable and fixed commits behaved consistently.
- Limitations: The proof uses the library API (
function_call) with a fakesmbclientbecause the CVE claims the vulnerability is reachable through authenticated VFS operations. The actual production entry point in a Horde application would call the sameHorde_Vfs_Smbmethods after authentication. - Environment: Ubuntu 26.04, PHP 8.5, Composer 2.9,
horde/Vfsrepository athttps://github.com/horde/Vfs.
CVE-2026-60102 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-60102
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-60102
Upgrade horde/vfs · github to 3.0.1 or later.
FAQ: CVE-2026-60102
How was CVE-2026-60102 demonstrated?
Which VFS operations can trigger CVE-2026-60102?
Which versions are affected by CVE-2026-60102, and where is it fixed?
How can I reproduce CVE-2026-60102?
References for CVE-2026-60102
Authoritative sources for CVE-2026-60102 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.