ANT-2026-05VXN1Y6 · wireshark/wireshark
heap-buffer-overflow low
Severity Claude high · Security research firm high · Maintainer low
Discovered by Claude Mythos Preview
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
- Craft RDP Dynamic Virtual Channel traffic carrying an EGFX PDU with a ZGFX-compressed segment.
- 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.
- Emit a run of literal tokens; each passes the
== 65535guard and writes controlled bytes past the 65536-byte outputSegment array. - 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
Triage and disclosure were performed by Ada Logics.
- Verdict
- true positive
- Severity
- high
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
Recorded dates, in order.
- 2026-04-02 Discovered or logged
- 2026-07-06 Sent to maintainer
- 2026-08-12 Maintainer acknowledged
- 2026-08-12 Patch released
- 2026-09-28 Publicly revealed
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"
}