curl · Report #3395666
libcurl MQTT PUBLISH length overflow (heap overflow)
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:
- Install 32‑bit toolchain and ASAN (Debian/Ubuntu):
sudo dpkg --add-architecture i386 && sudo apt updatesudo apt install gcc-multilib g++-multilib libc6-dev-i386 libasan6:i386
- Configure and build libcurl (from repo root):
mkdir build-asan32 && cd build-asan32cmake -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
- Start a minimal MQTT broker that replies CONNACK (separate shell):
python3 poc/fake_mqtt_broker.py 18830
- 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
- Run the PoC:
LD_LIBRARY_PATH=build-asan32/lib ./poc/poc_mqtt mqtt://127.0.0.1:18830/a
- Observe: AddressSanitizer reports a heap-buffer-overflow in
lib/mqtt.c::mqtt_publishat the payloadmemcpy, with allocation using a wrapped length.
Notes on prerequisites/limitations of this PoC:
- The PoC intentionally forces
CURLOPT_POSTFIELDSIZE_LARGEto 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 intoCURLOPT_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 largePOSTFIELDSIZE_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_LARGEvia upstream untrusted metadata such as HTTPContent-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.
- Primary: Ability to influence or set the size the application forwards to libcurl as the MQTT payload length (e.g., control over
- 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.