ANT-2026-EKGJXN5A · wireshark/wireshark

buffer-overflow medium

CVE-2026-19694

Severity Claude high · Security research firm high · Maintainer medium

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.

ANT-2026-EKGJXN5A: TTL reader fixed-offset write into undersized buffer

In ttl_read_segmented_message_entry(), item->size is read from the .ttl file and passed directly to g_try_malloc() with only an upper bound check and no minimum enforced. After reassembly (which the attacker can complete in a single segment by setting item->size as small as 2 and choosing the nested-frame type), the buffer is handed to ttl_check_segmented_message_recursion() and ttl_fix_segmented_message_entry_timestamp(), which perform raw memcpy() at fixed offsets with no size check. This yields an 8-byte OOB read at ttl.c:2016/2040 and an 8-byte OOB write of an attacker-controlled timestamp at buf+8 (ttl.c:2045). The attacker controls both the allocation size and the 8 bytes written, and can repeat the primitive per log entry, giving a multi-shot controlled heap corruption primitive triggered simply by opening a crafted capture in Wireshark.

Target

Project: wireshark/wireshark
Location: wiretap/ttl.c:2097
Discovery: static analysis — not yet dynamically reproduced

Technical Details

Root cause: item->size from the file header is trusted as the allocation size (g_try_malloc(item->size)) without enforcing that it is at least as large as the fixed header area the code later writes into. The subsequent memcpy(in->buf + sizeof(ttl_entryheader_t), &timestamp, 8) assumes ≥16 bytes and bypasses the bounds-checked helper ttl_read_bytes(), so a size of 2 results in 8 attacker-chosen bytes written past the heap chunk.

Reproduction

  1. Craft a .ttl file with a segmented message entry header advertising item->size = 2 and item->type = TTL_SEGMENTED_MESSAGE_ENTRY_TYPE_NESTED_FRAME.
  2. Set the first 2 bytes so the nested entry type resolves to TTL_BUS_DATA_ENTRY (0), and supply an arbitrary 64-bit timestamp value.
  3. Victim opens the file; ttl_read_entry() dispatches to ttl_read_segmented_message_entry(), which allocates 2 bytes via g_try_malloc().
  4. Reassembly completes in one segment; ttl_fix_segmented_message_entry_timestamp() memcpy()s the 8-byte attacker-controlled timestamp to buf+8, writing OOB.
  5. Repeat with multiple entries for a multi-shot heap corruption primitive.

[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]

Suggested Fix

Reject or clamp log-entry sizes that are smaller than the fixed header area before allocating and before performing any fixed-offset stores into the buffer.

Acknowledgement

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-EKGJXN5A.


Reference: ANT-2026-EKGJXN5A
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Ada Logics.

Verdict
true positive
Severity
high
UPSTREAM FIX

The change that resolved this finding.

diff --git a/wiretap/ttl.c b/wiretap/ttl.c
index e33c021c635..696eab17226 100644
--- a/wiretap/ttl.c
+++ b/wiretap/ttl.c
@@ -2009,10 +2009,15 @@ ttl_check_segmented_message_recursion(const ttl_read_t* in, int* err, char** err
 
     if (in->validity != VALIDITY_BUF) {
         *err = WTAP_ERR_INTERNAL;
-        *err_info = ws_strdup("tt_fix_segmented_message_entry_payload: input buffer is not valid");
+        *err_info = ws_strdup("ttl_check_segmented_message_recursion: input buffer is not valid");
         return false;
     }
 
+    if (sizeof(ttl_entryheader_t) > in->size - in->cur_pos) {
+        *err = WTAP_ERR_INTERNAL;
+        *err_info = ws_strdup("ttl_check_segmented_message_recursion: input buffer too short");
+        return false;
+    }
     memcpy(&header, in->buf + in->cur_pos, sizeof(ttl_entryheader_t));
     fix_endianness_ttl_entryheader(&header);
 
@@ -2033,14 +2038,23 @@ ttl_fix_segmented_message_entry_timestamp(const ttl_read_t* in, uint64_t timesta
 
     if (in->validity != VALIDITY_BUF) {
         *err = WTAP_ERR_INTERNAL;
-        *err_info = ws_strdup("tt_fix_segmented_message_entry_payload: input buffer is not valid");
+        *err_info = ws_strdup("ttl_fix_segmented_message_entry_timestamp: input buffer is not valid");
         return false;
     }
 
+    if (sizeof(ttl_entryheader_t) > in->size - in->cur_pos) {
+        goto buf_too_small;
+    }
     memcpy(&header, in->buf + in->cur_pos, sizeof(ttl_entryheader_t));
     fix_endianness_ttl_entryheader(&header);
 
     if ((header.size_type >> 12) == TTL_BUS_DATA_ENTRY) {
+        if (sizeof(uint64_t) > in->size - (in->cur_pos + sizeof(ttl_entryheader_t))) {
+        buf_too_small:
+            *err = WTAP_ERR_INTERNAL;
+            *err_info = ws_strdup("ttl_fix_segmented_message_entry_timestamp: input buffer too short");
+            return false;
+        }
         timestamp = GUINT64_TO_LE(timestamp);
         memcpy(in->buf + in->cur_pos + sizeof(ttl_entryheader_t), &timestamp, sizeof(uint64_t));
     }

https://github.com/wireshark/wireshark/commit/f9fdd24295456219f39be01beeaf734c2e3a8a2c

TIMELINE

Recorded dates, in order.

  1. 2026-04-02 Discovered or logged
  2. 2026-07-04 Sent to maintainer
  3. 2026-08-12 Maintainer acknowledged
  4. 2026-08-12 Patch released
  5. 2026-09-28 Publicly revealed
PROVENANCE

SHA-3-512 hash:

c760c779f7e6bc3898cd2b37f9232f2c382e5872f55457418cf3f6d6ca636a3c6601c1b8138d1c6551ef896021d5c6ac6f59e30e3d9519168fcdfd8ded48febd

Committed 2026-07-22 07:34 UTC

Revealed 2026-09-28 20:39 UTC

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-EKGJXN5A",
  "bug_class": "buffer_overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-04-16T14:11:18+00:00",
  "description": "In ttl_read_segmented_message_entry(), item->size is read from the .ttl file and passed directly to g_try_malloc() with only an upper bound check and no minimum enforced. After reassembly (which the attacker can complete in a single segment by setting item->size as small as 2 and choosing the nested-frame type), the buffer is handed to ttl_check_segmented_message_recursion() and ttl_fix_segmented_message_entry_timestamp(), which perform raw memcpy() at fixed offsets with no size check. This yields an 8-byte OOB read at ttl.c:2016/2040 and an 8-byte OOB write of an attacker-controlled timestamp at buf+8 (ttl.c:2045). The attacker controls both the allocation size and the 8 bytes written, and can repeat the primitive per log entry, giving a multi-shot controlled heap corruption primitive triggered simply by opening a crafted capture in Wireshark.",
  "discovered_at": "2026-04-02T00:00:00+00:00",
  "location": "wiretap/ttl.c:2097",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "wireshark/wireshark",
  "reproduction": [
    "1. Craft a .ttl file with a segmented message entry header advertising item->size = 2 and item->type = TTL_SEGMENTED_MESSAGE_ENTRY_TYPE_NESTED_FRAME.",
    "2. Set the first 2 bytes so the nested entry type resolves to TTL_BUS_DATA_ENTRY (0), and supply an arbitrary 64-bit timestamp value.",
    "3. Victim opens the file; ttl_read_entry() dispatches to ttl_read_segmented_message_entry(), which allocates 2 bytes via g_try_malloc().",
    "4. Reassembly completes in one segment; ttl_fix_segmented_message_entry_timestamp() memcpy()s the 8-byte attacker-controlled timestamp to buf+8, writing OOB.",
    "5. Repeat with multiple entries for a multi-shot heap corruption primitive."
  ],
  "technical_details": "Root cause: item->size from the file header is trusted as the allocation size (g_try_malloc(item->size)) without enforcing that it is at least as large as the fixed header area the code later writes into. The subsequent memcpy(in->buf + sizeof(ttl_entryheader_t), &timestamp, 8) assumes ≥16 bytes and bypasses the bounds-checked helper ttl_read_bytes(), so a size of 2 results in 8 attacker-chosen bytes written past the heap chunk.",
  "title": "TTL reader fixed-offset write into undersized buffer",
  "vendor_severity": "high"
}