HexBrain

curl · Report #4105853

libcurl TLS shorter-retry mismatch discloses client heap memory during cancelled IMAPS upload

Informativestatus
mediumseverity
3upvotes
19 Oct 2026disclosed

How it was found

Proof of Concept

High-level reproduction:

  1. Check out curl 8.22.0 at commit 01346829096c61b372692f6dc43ffa778c6caccd.
  2. Build libcurl with either:
    • rustls-ffi 0.15.4 / rustls 0.23.45; or
    • GnuTLS 3.8.13.
  3. Run the supplied loopback IMAPS server and multi-interface client.
  4. The client starts a large APPEND upload using a deliberately small socket send buffer.
  5. The server accepts the upload, stops reading until encrypted output blocks, then begins draining.
  6. The client legally cancels the active transfer using curl_multi_remove_handle().
  7. IMAP cleanup attempts to send a two-byte CRLF while the TLS backend still has plaintext from the previous larger write pending.
  8. The backend reports the previous write's byte count as consumed by the two-byte cleanup write.
  9. Curl_pp_flushsend() underflows its pointer/remaining-length arithmetic and passes out-of-bounds client memory back to TLS for encryption.

Observed results:

  • Rustls: UBSan confirmed pointer underflow and ASan confirmed a 16,384-byte heap over-read during TLS record encryption.
  • GnuTLS debug build: DEBUGASSERT((size_t)nwritten <= remain) failed.
  • GnuTLS non-debug build: UBSan confirmed the pointer underflow, followed by an invalid read inside GnuTLS/nettle encryption.
  • Non-sanitized controlled-canary runs proved network disclosure:
    • Rustls transmitted a fresh 32-byte client-memory canary as the start of a 16,384-byte TLS application-data record.
    • GnuTLS transmitted a separately calibrated 32-byte client-memory canary as the start of a 16,384-byte TLS application-data record.
  • The canaries were never supplied to a curl option, upload callback, libcurl send call, or socket write.

Controls:

  • Applying the current Rustls guard from PR #23260 prevented the Rustls disclosure.
  • Applying GnuTLS hardening commit 64fb3e7a7b116f690966054f281624152d4a7651 prevented the GnuTLS disclosure.
  • A normal upload without cancellation sent only the expected upload and two-byte CRLF terminator.
  • OpenSSL rejected the tested shorter-retry mismatch.

Description

Summary

libcurl can lose ownership of plaintext accepted during a nonblocking TLS send. A malicious IMAPS server can stall a large APPEND upload until TLS output is pending, then resume reading while the application legally cancels the transfer. IMAP cleanup sends a new two-byte CRLF command, but the affected TLS backend reports bytes accepted during the earlier, larger upload as consumed by this new call. curl trusts that oversized count, underflows pointer and remaining-length arithmetic, and reads client heap memory for encryption. We reproduced the invalid read with Rustls and GnuTLS and confirmed that controlled client-memory canaries were delivered to the TLS peer.

Public PR #23260 describes the Rustls shorter-retry symptom. This report additionally demonstrates the GnuTLS path and confirms concrete confidentiality impact for both backends.

Affected version

Confirmed target:

  • curl/libcurl 8.22.0
  • Git commit: 01346829096c61b372692f6dc43ffa778c6caccd
  • Platform: arm64 macOS 14.3
  • Compiler: Apple clang 15.0.0
  • CMake: 4.0.3

Confirmed TLS configurations:

libcurl/8.22.0-DEV rustls-ffi/0.15.4/rustls/0.23.45/aws-lc-rs
libcurl/8.22.0-DEV GnuTLS/3.8.13

OpenSSL rejected the tested retry mismatch. The complete affected version and backend range has not been established.

Steps To Reproduce

  1. Build the exact curl 8.22.0 commit with Rustls or GnuTLS.
  2. Compile the supplied libcurl multi-interface client against that exact build.
  3. Start the supplied loopback IMAPS server with the generated test certificate.
  4. Start the client, which uploads a large all-A IMAP literal.
  5. Let the server stop reading until the client's TLS/socket output blocks.
  6. Signal the server to begin draining.
  7. Call curl_multi_remove_handle() while the upload remains active.
  8. Observe the oversized TLS completion count being applied to IMAP's two-byte cleanup write.
  9. Under ASan/UBSan, observe the pointer underflow and invalid read.
  10. With the non-sanitized controlled allocator, verify that the peer receives the exact canary placed outside curl's requested command-buffer allocation.

Impact

Summary:

A malicious IMAPS server can cause an affected libcurl client to read memory outside the intended send buffer and encrypt that memory back to the server.

The disclosed memory belongs to the client process and may contain credentials, URLs, application data, cryptographic material, or data belonging to unrelated transfers. Controlled experiments confirmed transmission of exact client-memory canaries that were never provided for upload or passed to libcurl's send APIs. A default-allocator GnuTLS run also exposed natural process strings unrelated to the upload.

The issue can additionally crash or hang the client when the invalid pointer reaches unmapped memory.

Exploitation requires:

  • an affected TLS backend;
  • a sufficiently large IMAPS APPEND;
  • server-controlled backpressure; and
  • application cancellation of the active easy handle while TLS output remains pending.

No out-of-bounds write or code-execution primitive was demonstrated.

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