CVE-2024-20328: Command Injection Vulnerability in ClamAV's VirusEvent Feature

Executive Summary

CVE-2024-20328 is a command injection vulnerability discovered in the VirusEvent feature of ClamAV, a widely used open-source antivirus engine. This vulnerability allows a local attacker to execute arbitrary commands on the system with the privileges of the user running the ClamAV daemon (clamd). The root cause lies in the improper sanitization of file names when constructing command strings for execution. Attackers can exploit this issue when VirusEvent is enabled in configurations, effectively injecting malicious commands into the system.

The vulnerability has been addressed in software updates for affected ClamAV versions, and users are encouraged to patch their systems immediately. No workarounds are currently available; disabling vulnerable configurations or upgrading software is necessary to mitigate this vulnerability.

Technical Details

CVE-2024-20328 impacts the following versions of ClamAV:

  • All versions of ClamAV 0.104 (including patch versions)
  • ClamAV 0.105 (all patch versions)
  • ClamAV 1.0.0 through 1.0.4 (LTS)
  • ClamAV 1.1 (all patch versions)
  • ClamAV 1.2.0 and 1.2.1

The VirusEvent feature, configured in clamd.conf, enables users to execute custom commands when a virus is detected. These commands use placeholders like %v (virus name) and %f (file name), which are dynamically replaced during execution. However, the improper handling of %f leads to command injection.

For example, VirusEvent might be configured as:

VirusEvent "echo VIRUS DETECTED: %v in %f >> /dev/stdout"

Here, %f is replaced with the file name of the infected file. If the file name is crafted maliciously (e.g., containing shell commands), it may execute arbitrary code.

Code Vulnerability

The vulnerability resides in the virusaction function defined in clamd/clamd_others.c. Below is the relevant snippet of the vulnerable code:

void virusaction(const char *filename, const char *virname, const struct optstruct *opts) {
    ...
    buffer_cmd = (char *)calloc(len + v * strlen(virname) + f * strlen(filename) + 1, sizeof(char));
    if (!buffer_cmd) {
        ...
        return;
    }
    for (i = 0, j = 0; i < len; i++) {
        if (i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'f') {
            strcat(buffer_cmd, filename);
            j += strlen(filename);
            i++;
        } else {
            buffer_cmd[j++] = opt->strarg[i];
        }
    }
    ...
    _exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));
}

The issue lies in the unsafe handling of filename when constructing buffer_cmd. The content of filename—provided by external inputs—is directly appended to the command string without sanitization. If an attacker crafts a file name containing shell commands, they can execute those commands at the time of processing.

Root Cause Analysis

The root cause is a failure to validate and sanitize inputs in the virusaction function. The %f placeholder is replaced by the file name, which attackers can manipulate to include shell characters or commands. This vulnerability demonstrates an improper reliance on direct user-provided input to construct complex command strings.

To illustrate, consider the following malicious file name:

injectedfile;whoami;

When processed, the constructed command string becomes:

echo VIRUS DETECTED: [virus_name] in injectedfile;whoami; >> /dev/stdout

This leads to unintentional execution of the whoami command, and the output of the command injection would reveal sensitive details like the username associated with the ClamAV service.

The key vulnerable line enabling this behavior is:

_exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));

Here, the buffer_cmd is executed as a shell command without adequate scrutiny of its contents.

Patch Analysis

The ClamAV team addressed this issue by disabling the %f placeholder entirely. Instead, users must now rely on environment variables for file names. Below is an annotated analysis of the patch provided in commit 9ca550b0a5567ec697910635526196004b0a53dd:

Key Changes

File: clamd/clamd_others.c

+#define FILENAME_DISABLED_MESSAGE "The filename format character has been disabled due to security concerns, use the 'CLAM_VIRUSEVENT_FILENAME' environment variable instead."

...
-buffer_cmd = (char *)calloc(len + v * strlen(virname) + f * strlen(filename) + 1, sizeof(char));
+buffer_cmd = (char *)calloc(len + v * strlen(virname) + f * strlen(FILENAME_DISABLED_MESSAGE) + 1, sizeof(char));

...
-           strcat(buffer_cmd, filename);
-           j += strlen(filename);
+           strcat(buffer_cmd, FILENAME_DISABLED_MESSAGE);
+           j += strlen(FILENAME_DISABLED_MESSAGE);
  • The %f placeholder is replaced with a static warning string (FILENAME_DISABLED_MESSAGE), indicating that it has been disabled due to security concerns.
  • The usage of filename in strcat() is removed, eliminating any direct inclusion of user-controlled values in the command string.

File: common/optparser.c

-    {"VirusEvent", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD, "Execute a command when a virus is found. In the command string %v will be replaced with the virus name and %f will be replaced with the file name. Additionally, two environment variables will be defined: $CLAM_VIRUSEVENT_FILENAME and $CLAM_VIRUSEVENT_VIRUSNAME.", "/usr/bin/mailx -s \"ClamAV VIRUS ALERT: %v\" alert < /dev/null"},
+    {"VirusEvent", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD, "Execute a command when virus is found. Use the following environment variables to identify the file and virus names: - $CLAM_VIRUSEVENT_FILENAME - $CLAM_VIRUSEVENT_VIRUSNAME. ... (continues with details)", "/usr/bin/mailx -s \"ClamAV VIRUS ALERT: %v\" alert < /dev/null"},
  • Documentation is updated to reflect the changes. %f is no longer valid; users must now use $CLAM_VIRUSEVENT_FILENAME instead.

Exploitation Techniques

Proof of Concept (PoC)

Configuration:

VirusEvent "echo VIRUS DETECTED: %v in %f >> /dev/stdout"

Malicious File Name:

payload;id;

Exploit:

When an infected file with the above name is scanned, the following command gets executed:

echo VIRUS DETECTED: Multios.Coinminer-6781728 in payload;id; >> /dev/stdout

Output:

VIRUS DETECTED: Multios.Coinminer-6781728 in payload
uid=0(root) gid=0(root) groups=0(root)

Mitigation Strategies

  1. Immediate Patch Application:

    • Upgrade to ClamAV versions 1.2.2, 1.0.5, or 1.3.0 and above.
  2. Emergency Mitigation:

    • Disable the VirusEvent feature by commenting it out in the clamd.conf configuration file:
      # VirusEvent ...
      
  3. Security Best Practices:

    • Never execute commands that directly parse unsanitized inputs.
    • When relying on environment variables, validate their contents before using them in scripts or commands.

Timeline of Discovery and Disclosure

Date (2024) Event
January 2 Vulnerability discovered and reported to ClamAV team by Amit Schendel
February 7 Patch released by ClamAV team
February - March Public disclosure and widespread adoption of the patch

References

  1. ClamAV Patch Release Notes
  2. Amit Schendel's Technical Blog
  3. Official ClamAV Repository
  4. NVD Entry for CVE-2024-20328

Read more