HexBrain

curl · Report #3972403

CVE-2026-87864: IMAP APPEND plus CRLF conversion command injection

Validstatus
14 Oct 2026disclosed

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

  1. 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).

  2. 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.

  3. 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).

  4. 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
  1. Observe that curl exits with code 0 — it reports success.

  2. 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.

  3. 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.

  4. 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.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original