HexBrain

curl · Report #3395666

libcurl MQTT PUBLISH length overflow (heap overflow)

N/Astatus
lowseverity
9upvotes
28 Oct 2025disclosed

How it was found

Summary:

Heap-based buffer overflow in libcurl’s MQTT PUBLISH assembly (lib/mqtt.c::mqtt_publish) due to unchecked size_t arithmetic when computing the MQTT “Remaining Length”. If payloadlen + 2 + topiclen wraps size_t, libcurl allocates a too-small buffer and then memcpy’s payloadlen bytes into it, overflowing the heap. The bug triggers client-side immediately after CONNACK and does not require a malicious broker. Exploitability is high on 32-bit targets (or where size_t < curl_off_t); on 64-bit builds it tends to manifest as large allocation failures unless the caller severely misuses the API.

AI usage: The vulnerability was detected by SecMate (https://secmate.dev) and an AI assistant was used to help analyze code paths, prepare a PoC, and draft this report. All technical findings were validated locally with an ASAN-enabled build.

Affected version

  • Source: git master at commit a7ece53e96ba (rc-8_17_0-2-43-ga7ece53e9)
  • Platform used to reproduce: Ubuntu 22.04 (64‑bit host), building a 32‑bit libcurl with MQTT enabled and ASAN
  • Tooling: GCC 11.4.0, CMake 3.22.1
  • Build profile used for repro (CMake):
    • -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DBUILD_CURL_EXE=OFF
    • -DCURL_DISABLE_MQTT=OFF -DCURL_ENABLE_SSL=OFF -DCURL_DISABLE_NTLM=ON
    • -DCURL_USE_LIBPSL=OFF -DUSE_LIBIDN2=OFF -DUSE_NGHTTP2=OFF
    • -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF
    • -DCMAKE_C_FLAGS="-m32 -fsanitize=address -O1 -fno-omit-frame-pointer"
    • -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address -m32" -DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address -m32"

Steps To Reproduce:

  1. Install 32‑bit toolchain and ASAN (Debian/Ubuntu):
    • sudo dpkg --add-architecture i386 && sudo apt update
    • sudo apt install gcc-multilib g++-multilib libc6-dev-i386 libasan6:i386
  2. Configure and build libcurl (from repo root):
    • mkdir build-asan32 && cd build-asan32
    • cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DBUILD_CURL_EXE=OFF -DCURL_DISABLE_MQTT=OFF -DCURL_ENABLE_SSL=OFF -DCURL_DISABLE_NTLM=ON -DCURL_USE_LIBPSL=OFF -DUSE_LIBIDN2=OFF -DUSE_NGHTTP2=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCMAKE_C_FLAGS="-m32 -fsanitize=address -O1 -fno-omit-frame-pointer" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address -m32" -DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address -m32" ..
    • make -j
  3. Start a minimal MQTT broker that replies CONNACK (separate shell):
    • python3 poc/fake_mqtt_broker.py 18830
  4. Build the PoC client:
    • gcc -m32 -fsanitize=address -Iinclude -Lbuild-asan32/lib poc/mqtt_publish_overflow_poc.c -o poc/poc_mqtt -lcurl-d -Wl,-rpath,$PWD/build-asan32/lib
  5. Run the PoC:
    • LD_LIBRARY_PATH=build-asan32/lib ./poc/poc_mqtt mqtt://127.0.0.1:18830/a
  6. Observe: AddressSanitizer reports a heap-buffer-overflow in lib/mqtt.c::mqtt_publish at the payload memcpy, with allocation using a wrapped length.

Notes on prerequisites/limitations of this PoC:

  • The PoC intentionally forces CURLOPT_POSTFIELDSIZE_LARGE to an extreme value on a 32‑bit build to isolate the bug; that value is unrealistic for well‑behaved apps and exceeds MQTT’s Remaining Length. Realistic exploitation typically requires a “gateway misuse” pattern where an app forwards an attacker‑controlled size into CURLOPT_POSTFIELDSIZE(_LARGE) without enforcing sane bounds or MQTT limits, and without ensuring the in‑memory payload matches the size (or while running 32‑bit where the remaining length can wrap). On 64‑bit builds, this usually becomes a large allocation failure instead of an overflow.

Supporting Material/References:

[list any additional material (e.g. screenshots, logs, etc.)]

Supporting Material/References:

  • Advisory writeup: SEC-ADV-libcurl-MQTT-PUBLISH-length-overflow.md
  • PoC files:
    • poc/mqtt_publish_overflow_poc.c (client setting large POSTFIELDSIZE_LARGE)
    • poc/fake_mqtt_broker.py (minimal broker sending CONNACK)
    • poc/README.md (build/run steps, threat model, limitations, realistic gateway example)
  • ASAN crash excerpt:
ERROR: AddressSanitizer: heap-buffer-overflow
    in mqtt_publish (lib/mqtt.c:629) during memcpy(&pkt[i], payload, payloadlen)
    allocation at lib/mqtt.c:617 used wrapped length (remaininglength + 1 + encodelen)
    called from mqtt_doing (lib/mqtt.c:949)

Impact

Summary:

  • Technical impact: Heap-based buffer overflow during MQTT PUBLISH packet assembly in the client. Reliable denial-of-service (crash) on affected builds; memory corruption with potential for further impact depends on allocator/hardening and environment.
  • Attacker capabilities required:
    • Primary: Ability to influence or set the size the application forwards to libcurl as the MQTT payload length (e.g., control over CURLOPT_POSTFIELDSIZE/CURLOPT_POSTFIELDSIZE_LARGE via upstream untrusted metadata such as HTTP Content-Length, JSON “size” fields, CLI/config, or plugin inputs).
    • No broker control is required; a benign or fake broker that sends CONNACK is sufficient to drive the code path. A MITM can accelerate/guarantee this state but cannot set the lengths by itself.
    • Platform precondition: Most practical on 32-bit builds (or where size_t < curl_off_t), common in embedded/legacy systems or certain CI/containers. On 64-bit, this typically manifests as large allocation behavior rather than overflow unless the caller severely misuses the API.
  • Affected scope: Applications using libcurl with mqtt:// and POST. The issue is independent of TLS; it occurs before any PUBLISH is put on the wire.

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