ClamAV's configuration allows admins to run a command when a virus is found. By failing to sanitize the filename placeholder (%f) before passing it to 'sh -c', the engine executes any shell commands embedded in the name of the infected file. It's a 1990s-style vulnerability in a 2024 security product.
A classic OS Command Injection vulnerability in ClamAV's 'VirusEvent' feature allows local attackers to execute arbitrary code by simply naming a file with malicious shell characters.
Antivirus software holds a privileged place in our infrastructure. It runs on everything—mail gateways, file servers, CI/CD pipelines—often with root or specialized service privileges. We trust it to inspect the untrusted. But what happens when the inspector itself is gullible?
Enter the VirusEvent directive in clamd.conf. It’s a convenience feature designed for system administrators who want to automate alerts. The premise is simple: "Hey ClamAV, if you find a virus, run this script to send me an email or page my pager." (Yes, sysadmins still use pagers in spirit).
To make this useful, ClamAV provides variable substitution. You can use %v for the virus name and %f for the filename. It sounds innocent enough, a standard string interpolation feature you'd find in any logging tool. But in the world of C programming and shell execution, "string interpolation" is often just a polite euphemism for "arbitrary code execution waiting to happen."
The vulnerability (CVE-2024-20328) is a textbook case of CWE-78: OS Command Injection. It stems from a fundamental misunderstanding of how dangerous the UNIX shell is when handling untrusted input.
When clamd detects a malicious file, it looks up the VirusEvent string. If configured, it performs a naive search-and-replace. It takes the path of the infected file—which is attacker-controlled—and stuffs it directly into the command string where %f used to be. There is no sanitization, no escaping, and no parameterization.
The resulting string is then passed to /bin/sh -c. This is the critical failure. If the filename is just virus.exe, the command becomes echo virus.exe detected. Harmless. But if the filename is virus.exe; rm -rf /;, the shell sees the semicolon, terminates the first command, and happily executes the second one. The antivirus engine effectively becomes a proxy for the attacker's commands.
Let's look at the crime scene in clamd/clamd_others.c. The vulnerable function is virusaction. Here is a simplified view of the logic flow that doomed the daemon:
// The Vulnerable Logic
// buffer_cmd contains the config string with %f replaced by the filename
// env is the environment setup
pid_t pid = fork();
if (pid == 0) {
// Child process
// The Fatal Flaw: Passing the concatenated string to sh -c
_exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));
}The developer used execle, which is a variadic function to execute a program. They correctly called /bin/sh. However, by passing -c followed by the dirty buffer_cmd, they invoked the shell's interpreter mode.
A secure implementation would have avoided the shell entirely (using execve on the target script directly) or properly escaped the input. The reliance on sh -c turned a formatting string into an execution context.
Exploiting this requires two things: a system with VirusEvent enabled (common in enterprise logging setups) and the ability to write a file to a directory scanned by ClamAV.
Step 1: The Setup
Assume the admin has configured: VirusEvent "wall 'Found virus %v in %f'".
Step 2: The Payload
We need a file that triggers a virus detection (so VirusEvent fires) but has a filename that executes code. We'll use the EICAR test string for the content.
Step 3: The Attack
The attacker creates a file named:
safe_file; id > /tmp/pwned; #
Inside the file, they simply place the EICAR string: X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
Step 4: The Trigger
When ClamAV scans this file, it detects the EICAR signature. It constructs the command:
sh -c "wall 'Found virus Eicar-Test-Signature in safe_file; id > /tmp/pwned; #'"
The shell interprets this as:
wall 'Found ... safe_fileid > /tmp/pwned (The Payload)# ... (Comment out the rest)Boom. Code execution with the privileges of the ClamAV daemon.
While the CVSS score is a modest 5.3 (due to the requirement for local access and specific configuration), the real-world impact can be catastrophic depending on the deployment.
Privilege Escalation: In many setups, clamd runs as a dedicated user, but in some lazy configurations (or containerized environments), it runs as root. Even as the clamav user, an attacker can often modify quarantine directories, access logs, or pivot to other parts of the system.
Remote Vectors: Consider a mail gateway. If the gateway saves attachments to disk using their original filenames (a bad practice, but it happens) before scanning, a remote attacker could potentially trigger this by emailing a specifically named attachment. If the VirusEvent fires on the temp path, and the temp path preserves the filename... game over.
The remediation is straightforward: stop trusting filenames. The ClamAV team addressed this in versions 1.3.0, 1.2.2, and 1.0.5.
The fix involves removing the ability for %f to inject shell characters, or in some contexts, deprecating that substitution entirely where it cannot be done safely. The patch ensures that arguments passed to the event handler are treated as data, not code.
Mitigation Strategy:
clamd.conf and remove %f from your VirusEvent directive. Use environment variables if your version supports passing metadata safely, or stick to static alerts.CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
ClamAV Cisco | 1.2.0 - 1.2.1 | 1.2.2 |
ClamAV Cisco | 1.0.0 - 1.0.4 | 1.0.5 |
ClamAV Cisco | <= 0.105.x | 1.0.5 |
| Attribute | Detail |
|---|---|
| CWE | CWE-78 (OS Command Injection) |
| Attack Vector | Local (potentially Remote via file upload) |
| CVSS v3.1 | 5.3 (Medium) |
| Impact | Arbitrary Code Execution / Privilege Escalation |
| Vulnerable Component | clamd daemon (VirusEvent) |
| Exploit Status | Proof of Concept Available |
The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
Get the latest CVE analysis reports delivered to your inbox.