curl · Report #3972403
CVE-2026-87864: IMAP APPEND plus CRLF conversion command injection
How it was found
Summary
When CURLOPT_CRLF is enabled, imap_perform_append() advertises the original input file size in the IMAP APPEND ... {N} literal but then transmits the expanded body (bare LF → CRLF). The server consumes exactly N bytes as the literal, and the overflow bytes are parsed as new IMAP commands in the same authenticated session.
This is a protocol framing bug — curl tells the server "here come N bytes," then sends more than N — not a case of the user injecting CRLFs into a protocol field. The bytes originate in the upload body, and curl's own conversion moves them across the literal boundary.
Affected versions
Tested and confirmed on commit c2676bf9e639e31e44b12a2cc82cf2046231b056 (curl 8.22.0-DEV) and curl 8.6.0. The mismatch likely dates back to IMAP APPEND's introduction in 7.30.0.
Root cause
In lib/imap.c, imap_perform_append() sets up the upload reader with the original size and sends it in the IMAP literal:
result = Curl_creader_set_fread(data, data->state.infilesize);
...
result = imap_sendf(data, imapc, "APPEND %s%s {%" FMT_OFF_T "}",
mailbox, curlx_dyn_ptr(&flags), data->state.infilesize);
The cr-lineconv reader (lib/sendf.c) converts each bare LF to CRLF and explicitly returns -1 for its total length because the converted size is unknown. Curl_req_send_more() (lib/request.c) drains the reader until EOS — it does not stop at the IMAP-declared boundary.
An N-byte input containing k bare LFs produces N+k wire bytes while the server was told to consume only N.
Reproduction
-
Build curl at commit c2676bf9e639e31e44b12a2cc82cf2046231b056 (or any version from 7.30.0 onward) on a Linux or macOS host, without TLS (plain IMAP is sufficient for reproduction).
-
Create a 66-byte upload file consisting of A\n repeated 22 times (44 bytes), followed by the 22-byte suffix \nX CREATE "Injected"\r\n. The 22 bare LFs before the suffix are chosen so that CRLF expansion adds exactly 22 bytes — pushing the suffix past the declared literal boundary.
-
Start a local IMAP server on port 11430 that implements standard IMAP framing: respond to CAPABILITY, LOGIN, and APPEND. On APPEND, parse the {N} literal size from the command, send + continue, then consume exactly N bytes from the socket as the literal body. After consuming the literal and its terminating CRLF, read and record the next line received (the first post-literal command).
-
Run curl with CRLF conversion enabled, uploading the payload via IMAP APPEND:
curl -sS --user user:pass --upload-file payload.bin --crlf \
imap://127.0.0.1:11430/INBOX
-
Observe that curl exits with code 0 — it reports success.
-
Check what the IMAP server received after the literal — it will have parsed X CREATE "Injected" as a standalone IMAP command, separate from the APPEND literal. This proves that bytes from the upload body escaped the declared literal boundary and were interpreted as IMAP protocol commands.
-
Run a control case without --crlf using the same payload and an identical IMAP server on a different port. Without CRLF conversion, the entire 66-byte payload fits within the declared 66-byte literal. The first post-literal command will be curl's normal LOGOUT, and the X CREATE "Injected" text remains safely inside the message body.
-
Compare the two cases — in both, curl declared a 66-byte literal in the APPEND command. With --crlf, the 22 bare LFs expanded to CRLFs, producing 88 wire bytes against a 66-byte declaration. The server consumed 66 bytes as the literal, and the remaining 22 bytes (\r\nX CREATE "Injected"\r\n) were parsed as protocol — the CRLF terminated the APPEND and X CREATE "Injected" executed as a new command.
Suggested fix
Reject the CURLOPT_CRLF / CURLOPT_TRANSFERTEXT + IMAP APPEND combination with an explicit error, or pre-compute the converted size before sending the APPEND literal declaration. Truncating output at the declared length would prevent command escape but silently corrupt the message — an explicit failure is preferable.
Impact
Summary:
An attacker who controls the content of an uploaded message can inject arbitrary IMAP commands when:
- the application enables CRLF conversion (
CURLOPT_CRLF/--crlf), - the upload has a known size (required by IMAP APPEND in curl),
- curl performs an authenticated IMAP APPEND, and
- the attacker can control the upload bytes with byte-level precision.
The injected commands execute with the authenticated IMAP user's full privileges (CREATE, DELETE, STORE, COPY, etc.). CURLOPT_TRANSFERTEXT=1 on Unix reaches the same code path.
Conversion is disabled by default, which limits exposure, but the framing violation itself is unconditional whenever conversion is active.