ANT-2026-05VXN1Y6 · wireshark/wireshark

heap-buffer-overflow low

CVE-2026-76888

Severity Claude high · Security research firm high · Maintainer low

Discovered by Claude Mythos Preview

REPORT

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

ANT-2026-05VXN1Y6: RDP ZGFX decompressor history buffer overflow

Wireshark's RDP ZGFX decompressor (epan/tvbuff_rdp.c) guards literal writes with if (zgfx->outputCount == 65535) return false; instead of >=. A match token encoding count=65536 passes both the caller check at :452 and the unsigned-wrapped check at :292, filling the 65536-byte outputSegment exactly and setting outputCount=65536. Every subsequent literal token then bypasses the == 65535 guard and writes attacker-controlled bytes at outputSegment[65536], [65537], ... past the end of the wmem_file_scope-allocated zgfx_context_t. The code is reached with no preconditions from the default-registered RDP EGFX / DRDYNVC dissectors on raw packet bytes, giving a remote attacker a controlled heap overwrite leading to code execution.

Target

Project: wireshark/wireshark
Location: epan/tvbuff_rdp.c:244
Discovery: static analysis — not yet dynamically reproduced

Technical Details

The root cause is an off-by-one equality comparison at tvbuff_rdp.c:241 (outputCount == 65535) combined with unsigned arithmetic wrap in the bounds check at :292 (count > sizeof(outputSegment) - outputCount wraps when outputCount is already 65536) and a <=-equivalent check at :452 that admits count==65536. Together these allow outputCount to reach 65536, after which zgfx_write_literal() writes past the last field of the heap-allocated zgfx_context_t, corrupting adjacent heap metadata / tvbuff pointers.

Reproduction

  1. Craft RDP Dynamic Virtual Channel traffic carrying an EGFX PDU with a ZGFX-compressed segment.
  2. In the compressed stream, emit a match token whose distance is valid in the history buffer and whose decoded length is exactly 65536, filling outputSegment and setting outputCount=65536.
  3. Emit a run of literal tokens; each passes the == 65535 guard and writes controlled bytes past the 65536-byte outputSegment array.
  4. Use the overwritten adjacent heap data (wmem chunk headers / tvbuff ops pointers) to gain control when Wireshark continues processing.

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

Suggested Fix

Bound all writes into the ZGFX output/history buffers with range (>=) comparisons against the buffer size, and rewrite the bounds-check arithmetic so it cannot underflow/wrap (e.g., compare outputCount + count > sizeof(outputSegment) using a wide-enough type, or check outputCount >= sizeof(outputSegment) before subtracting).

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-05VXN1Y6.


Reference: ANT-2026-05VXN1Y6
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/epan/tvbuff_rdp.c b/epan/tvbuff_rdp.c
index 5f8f06c9c25..5c19766f1bb 100644
--- a/epan/tvbuff_rdp.c
+++ b/epan/tvbuff_rdp.c
@@ -253,7 +253,7 @@ zgfx_write_raw(zgfx_context_t *zgfx, bitstream_t *b, uint32_t count)
 	uint32_t rest, tocopy;
 
 	/* first copy in the output buffer */
-	if (zgfx->outputCount > 65535 - count)
+	if (count > 65535 - zgfx->outputCount)
 		return false;
 
 	if (!bitstream_copyraw(b, &(zgfx->outputSegment[zgfx->outputCount]), count))
@@ -289,7 +289,7 @@ zgfx_write_from_history(zgfx_context_t *zgfx, uint32_t distance, uint32_t count)
 	uint32_t remainingCount, copyTemplateSize, toCopy;
 	uint8_t *outputPtr;
 
-	if (zgfx->outputCount > 65535 - count)
+	if (count > 65535 - zgfx->outputCount)
 		return false;
 
 	remainingCount = count;
@@ -340,7 +340,7 @@ rdp8_decompress_segment(zgfx_context_t *zgfx, tvbuff_t *tvb)
 
 	if (!(flags & ZGX_PACKET_COMPRESSED)) {
 		if (len > 65535) {
-		    return false;
+			return false;
 		}
 		tvbuff_t *raw = tvb_new_subset_remaining(tvb, 1);
 		zgfx_write_history_buffer_tvb(zgfx, raw, len);
@@ -516,7 +516,7 @@ rdp8_decompress(zgfx_context_t *zgfx, wmem_allocator_t *allocator, tvbuff_t *tvb
 			if (!rdp8_decompress_segment(zgfx, tvb_new_subset_length(tvb, offset, segment_size))) {
 				wmem_free(allocator, output);
 				return NULL;
-                        }
+			}
 
 			output_consumed += zgfx->outputCount;
 			if (output_consumed > uncompressed_size) {

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

TIMELINE

Recorded dates, in order.

  1. 2026-04-02 Discovered or logged
  2. 2026-07-06 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:

84705f9a0441a982ed200ee2d190ff6c9f07eb0b823b85df3607f163387a69d0f4318f87f1ec0e2125eab1241a13c976aaf22df176b5ab5871fade5de62af643

Committed 2026-07-22 07:34 UTC

Revealed 2026-09-28 20:40 UTC

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-05VXN1Y6",
  "bug_class": "Heap Buffer Overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-04-16T14:11:15+00:00",
  "description": "Wireshark's RDP ZGFX decompressor (epan/tvbuff_rdp.c) guards literal writes with `if (zgfx->outputCount == 65535) return false;` instead of `>=`. A match token encoding count=65536 passes both the caller check at :452 and the unsigned-wrapped check at :292, filling the 65536-byte outputSegment exactly and setting outputCount=65536. Every subsequent literal token then bypasses the `== 65535` guard and writes attacker-controlled bytes at outputSegment[65536], [65537], ... past the end of the wmem_file_scope-allocated zgfx_context_t. The code is reached with no preconditions from the default-registered RDP EGFX / DRDYNVC dissectors on raw packet bytes, giving a remote attacker a controlled heap overwrite leading to code execution.",
  "discovered_at": "2026-04-02T00:00:00+00:00",
  "location": "epan/tvbuff_rdp.c:244",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "wireshark/wireshark",
  "reproduction": [
    "1. Craft RDP Dynamic Virtual Channel traffic carrying an EGFX PDU with a ZGFX-compressed segment.",
    "2. In the compressed stream, emit a match token whose distance is valid in the history buffer and whose decoded length is exactly 65536, filling outputSegment and setting outputCount=65536.",
    "3. Emit a run of literal tokens; each passes the `== 65535` guard and writes controlled bytes past the 65536-byte outputSegment array.",
    "4. Use the overwritten adjacent heap data (wmem chunk headers / tvbuff ops pointers) to gain control when Wireshark continues processing."
  ],
  "technical_details": "The root cause is an off-by-one equality comparison at tvbuff_rdp.c:241 (`outputCount == 65535`) combined with unsigned arithmetic wrap in the bounds check at :292 (`count > sizeof(outputSegment) - outputCount` wraps when outputCount is already 65536) and a `<=`-equivalent check at :452 that admits count==65536. Together these allow outputCount to reach 65536, after which zgfx_write_literal() writes past the last field of the heap-allocated zgfx_context_t, corrupting adjacent heap metadata / tvbuff pointers.",
  "title": "RDP ZGFX decompressor history buffer overflow",
  "vendor_severity": "high"
}