Skip to content

fix: prevent UAF in MQTT packet processing loop#5

Open
goyalpalak18 wants to merge 1 commit intohoneynet:mainfrom
goyalpalak18:fix/mqtt-uaf-packet-loop
Open

fix: prevent UAF in MQTT packet processing loop#5
goyalpalak18 wants to merge 1 commit intohoneynet:mainfrom
goyalpalak18:fix/mqtt-uaf-packet-loop

Conversation

@goyalpalak18
Copy link
Copy Markdown

Description

I ran into a critical Use-After-Free (UAF) bug in the MQTT packet processing loop (servers/mqtt_pit.c) that was causing the tarpit process to crash when handling multi-packet TCP segments.

I noticed that automated IoT scanners (like Mirai derivatives or Masscan) routinely concatenate packets like CONNECT+DISCONNECT or DISCONNECT+PING in a single segment. When the tarpit receives these, it crashes, resulting in the silent termination of all active tarpitted connections and the loss of session duration data and packet captures without triggering any metrics alarms.

Root Cause

While debugging the main() epoll loop, I saw that disconnectClient() is called inside several case branches of the switch(request) statement. This function fully deallocates the client struct via free(). However, the subsequent break statement only exits the switch, not the enclosing for loop iterating over packetCount.

If a disconnect occurs on iteration i < packetCount - 1, the next loop iteration executes against the freed client pointer, causing UAF reads and writes. Additionally, I found that once the loop exits, a post-loop block unconditionally calculates leftover bytes and writes to client->bytesWrittenToBuffer, overwriting glibc's heap freelist metadata. This corrupts the heap state and crashes the process on the next malloc() call (usually the next accept()).

Changes Proposed

I implemented state tracking for the client pointer's lifetime within the processing loop to fix this:

  • Added a bool clientFreed = false; flag before the packet processing loop.
  • Set clientFreed = true immediately before the 5 disconnectClient() calls inside the switch branches (CONNECT failures, PUBCOMP, PING, DISCONNECT).
  • Added an if (clientFreed) break; check after the switch statement to exit the for loop before the freed pointer is accessed again.
  • Guarded the post-loop buffer compaction logic (lines 979–985) with if (!clientFreed) to prevent the final heap overwrite.

Set clientFreed flag before each disconnectClient() call and break
out of the for loop immediately after, preventing subsequent
iterations from accessing the freed client struct. Guard the
post-loop buffer cleanup with the same flag.

Signed-off-by: goyalpalak18 <goyalpalak1806@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant