ANT-2026-PCBAKVPB · dnsmasq

heap-buffer-overflow high

CVE-2026-4892 GHSA-m62j-63mf-xr95

Severity Claude high · Security research firm high · Maintainer high

Discovered by an unreleased Anthropic model

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Trail of Bits.

ANT-2026-PCBAKVPB: Heap overflow in privileged helper via oversized DHCPv6 client identifier

In dnsmasq's privileged script helper (helper.c:265-270), each byte of the DHCP client-ID is hex-encoded with sprintf into daemon->packet, a ~2267-byte buffer allocated at startup and inherited via fork(). DHCPv6 option lengths are 16-bit and state->clid_len is taken from the wire at rfc3315.c:338 with no upper bound, then flows unchanged through lease_set_hwaddr() and queue_script() to the helper. A local-network attacker sends a single DHCPv6 SOLICIT with RAPID_COMMIT and a ~1200-byte OPTION6_CLIENT_ID (fits in one unfragmented IPv6 frame); when the lease script event fires, the helper writes ~3599 bytes into the 2267-byte buffer. The attacker controls the overflow length and the hex-digit content, corrupting the heap of a process that runs as root by default.

Target

Project: dnsmasq
Version: commit b95d5a25777cc8910d2c2a921c68c778a3b30498 (still present at HEAD)
Location: src/helper.c:265

Technical Details

Sink confirmed via ASAN: a harness allocating daemon->packet exactly as dnsmasq.c:128-129 does (PACKETSZ+MAXDNAME+RRFIXEDSZ = 1547 bytes at default edns_pktsz) and running the exact helper.c:265-270 hex-encode loop with clid_len=800 produced 'AddressSanitizer: heap-buffer-overflow … WRITE of size 3 … 0 bytes after 1547-byte region allocated by safe_malloc'. Source path verified statically: rfc3315.c:338 assigns state->clid_len = opt6_len(opt) with no upper bound; lease_set_hwaddr() (lease.c:992) stores it unchecked; queue_script() (helper.c:794/809) forwards it to the helper pipe unchanged. The helper inherits daemon->packet via fork(). Overflow threshold is clid_len ≥ ceil((bufsz+1)/3) ≈ 516 at defaults — well within the 16-bit DHCPv6 option length. Did not stand up a full DHCPv6 transaction in the time box, hence medium confidence on end-to-end reachability (requires --dhcp-script configured).

Crash trace (truncated — full trace in attached crash.log):

daemon->packet size = 1547 (PACKETSZ=512 MAXDNAME=1025 RRFIXEDSZ=10)
=================================================================
==17==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x51b000000d8b at pc 0xffff86e829d4 bp 0xffffc5315490 sp 0xffffc5315530
WRITE of size 3 at 0x51b000000d8b thread T0
    #0 0xffff86e829d0 in vsprintf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1671
    #1 0xffff86e83cf0 in sprintf ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1714
    #2 0xaaaab6b5ca6c in main /build/harness_400509.c:35
    #3 0xffff863c2258  (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 45918bc10b33fd96afc550c98de062dccdf44328)
    #4 0xffff863c2338 in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 45918bc10b33fd96afc550c98de062dccdf44328)
    #5 0xaaaab6b5c5ec in _start (/build/h509+0x24c5ec) (BuildId: 59c1c64e9de91afd8fd09c5c0f7cb8edeff03a60)

allocated by thread T0 here:
    #0 0xffff86ea9e4c in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
    #1 0xaaaab6cbe2bc in safe_malloc /build/dnsmasq/src/util.c:321
    #2 0xaaaab6b5c7bc in main /build/harness_400509.c:21
    #3 0xffff863c2258  (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 45918bc10b33fd96afc550c98de062dccdf44328)
    #4 0xffff863c2338 in __libc_start_main (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 45918bc10b33fd96afc550c98de062dccdf44328)
    #5 0xaaaab6b5c5ec in _start (/build/h509+0x24c5ec) (BuildId: 59c1c64e9de91afd8fd09c5c0f7cb8edeff03a60)

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1671 in vsprintf

Reproduction

  1. Craft a DHCPv6 SOLICIT containing OPTION6_RAPID_COMMIT, a minimal IA_TA (4-byte header + 4-byte IAID), and OPTION6_CLIENT_ID with a ≥756-byte (e.g. 1200-byte) payload
  2. Send it to the dnsmasq DHCPv6 port; packet fits in a single 1280-byte-MTU IPv6 frame
  3. RAPID_COMMIT path (rfc3315.c:654-660) sets lease_allocate=1; server allocates an address and stores clid/clid_len verbatim in the lease
  4. do_script_run() fires and queue_script() pipes clid_len to the helper child
  5. Helper reads data.clid_len and runs the sprintf hex-encode loop at helper.c:265-270, writing ~3×clid_len bytes into the fixed daemon->packet buffer and overflowing it by ~1332 bytes

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

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-PCBAKVPB.


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

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Trail of Bits.

Verdict
true positive
Severity
high
TIMELINE

Dates from discovery through public reveal.

  1. 2026-04-08 Reported to tracker
  2. 2026-05-07 Sent to maintainer
  3. 2026-05-09 Maintainer acknowledged
  4. 2026-05-09 Patch released
  5. 2026-08-17 Publicly revealed
PROVENANCE

SHA-3-512 hash:

11a65d2d8b73e1b60aecd4aa985a653c77e1870999845708ab73eb335b44073062498a75e0adff81fdda190aa5e9610c21c27d36961cc01f8493cf85f7e3fa81

Committed 2026-05-07 00:02 PT

Revealed 2026-08-17 10:47 PT

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-PCBAKVPB",
  "bug_class": "Heap-buffer-overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-04-09T05:32:58+00:00",
  "description": "In dnsmasq's privileged script helper (helper.c:265-270), each byte of the DHCP client-ID is hex-encoded with sprintf into daemon->packet, a ~2267-byte buffer allocated at startup and inherited via fork(). DHCPv6 option lengths are 16-bit and state->clid_len is taken from the wire at rfc3315.c:338 with no upper bound, then flows unchanged through lease_set_hwaddr() and queue_script() to the helper. A local-network attacker sends a single DHCPv6 SOLICIT with RAPID_COMMIT and a ~1200-byte OPTION6_CLIENT_ID (fits in one unfragmented IPv6 frame); when the lease script event fires, the helper writes ~3599 bytes into the 2267-byte buffer. The attacker controls the overflow length and the hex-digit content, corrupting the heap of a process that runs as root by default.",
  "discovered_at": null,
  "location": "src/helper.c:265",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "dnsmasq",
  "reproduction": [
    "1. Craft a DHCPv6 SOLICIT containing OPTION6_RAPID_COMMIT, a minimal IA_TA (4-byte header + 4-byte IAID), and OPTION6_CLIENT_ID with a ≥756-byte (e.g. 1200-byte) payload",
    "2. Send it to the dnsmasq DHCPv6 port; packet fits in a single 1280-byte-MTU IPv6 frame",
    "3. RAPID_COMMIT path (rfc3315.c:654-660) sets lease_allocate=1; server allocates an address and stores clid/clid_len verbatim in the lease",
    "4. do_script_run() fires and queue_script() pipes clid_len to the helper child",
    "5. Helper reads data.clid_len and runs the sprintf hex-encode loop at helper.c:265-270, writing ~3×clid_len bytes into the fixed daemon->packet buffer and overflowing it by ~1332 bytes"
  ],
  "technical_details": "Sink confirmed via ASAN: a harness allocating daemon->packet exactly as dnsmasq.c:128-129 does (PACKETSZ+MAXDNAME+RRFIXEDSZ = 1547 bytes at default edns_pktsz) and running the exact helper.c:265-270 hex-encode loop with clid_len=800 produced 'AddressSanitizer: heap-buffer-overflow … WRITE of size 3 … 0 bytes after 1547-byte region allocated by safe_malloc'. Source path verified statically: rfc3315.c:338 assigns state->clid_len = opt6_len(opt) with no upper bound; lease_set_hwaddr() (lease.c:992) stores it unchecked; queue_script() (helper.c:794/809) forwards it to the helper pipe unchanged. The helper inherits daemon->packet via fork(). Overflow threshold is clid_len ≥ ceil((bufsz+1)/3) ≈ 516 at defaults — well within the 16-bit DHCPv6 option length. Did not stand up a full DHCPv6 transaction in the time box, hence medium confidence on end-to-end reachability (requires --dhcp-script configured).",
  "title": "Heap overflow in privileged helper via oversized DHCPv6 client identifier",
  "vendor_severity": "high"
}