Add Nmap IPv6 extension length wrap PoC

This commit is contained in:
ashton
2026-06-23 03:37:31 -05:00
parent cd7ac77828
commit 18a1adb9ba
5 changed files with 337 additions and 1 deletions

View File

@@ -2,7 +2,7 @@
A consolidated archive of my public proof-of-concept and vulnerability research writeups. A consolidated archive of my public proof-of-concept and vulnerability research writeups.
Each folder contains one of my former standalone PoC repos, preserved with its original README and tracked files. This repo is just a cleaner single-home layout for the same work. Most folders contain one of my former standalone PoC repos, preserved with its original README and tracked files. New research entries may also be added directly here.
## Contents ## Contents
@@ -17,6 +17,7 @@ Each folder contains one of my former standalone PoC repos, preserved with its o
| `imagemagick-gs-delegate-hijack-poc` | `8140e8ee0ed78beaf5e8303a795b70b138f5891b` | 5 | | `imagemagick-gs-delegate-hijack-poc` | `8140e8ee0ed78beaf5e8303a795b70b138f5891b` | 5 |
| `lunar-modrinth-chain-poc` | `ffd02120708b6503f11585858ce3724872f3b7a7` | 6 | | `lunar-modrinth-chain-poc` | `ffd02120708b6503f11585858ce3724872f3b7a7` | 6 |
| `mybb-limited-acp-to-admin` | `1610e0373943c2f6562a99f917d3a3d1fdd9056d` | 5 | | `mybb-limited-acp-to-admin` | `1610e0373943c2f6562a99f917d3a3d1fdd9056d` | 5 |
| `nmap-ipv6-extlen-wrap-poc` | `new research entry` | 4 |
| `objdump-dlx-calc-poc` | `7df01e4e20c7375a89e8ccf760526c52eb6ad582` | 41 | | `objdump-dlx-calc-poc` | `7df01e4e20c7375a89e8ccf760526c52eb6ad582` | 41 |
| `openvpn-connect-echo-script-ace-poc` | `d2f904d9272d4388c9862131d40e32e072e85e38` | 8 | | `openvpn-connect-echo-script-ace-poc` | `d2f904d9272d4388c9862131d40e32e072e85e38` | 8 |
| `vlc-vp9-reschange-crash-poc` | `fae72b82f24d03cf2fb9cb55fbb2e7774f684ff3` | 3 | | `vlc-vp9-reschange-crash-poc` | `fae72b82f24d03cf2fb9cb55fbb2e7774f684ff3` | 3 |

View File

@@ -0,0 +1,115 @@
# Nmap IPv6 extension length wrap PoC
Compact proof of concept for an IPv6 extension-header parser length-wrap condition in Nmap's shared packet parsing code.
Research status: ongoing.
## Summary
The PoC models the IPv6 parser path in `libnetutil/netutil.cc` and the packet-length adjustment behavior in `tcpip.cc`.
A crafted IPv6 packet can declare an extension header that is only partially present in the captured packet. The parser checks that two extension-header bytes are available, uses the second byte as the extension-header length, advances the payload pointer by that declared length, and then stores the remaining payload length in an unsigned integer.
With the packet shape in this PoC:
```text
captured packet length: 48
IPv6 payload length: 8
IPv6 next header: Hop-by-Hop Options
extension next header: UDP
extension header length field: 1
```
The parser advances the payload pointer to offset 56, which is beyond the 48-byte capture, then computes the payload length as an unsigned wraparound value.
Observed local harness result:
```text
helper_returned=true
next_header=17
payload_offset=56
wrapped_payload_len=4294967288
validator_len_after_adjust=64
captured_len=48
```
That demonstrates a malformed 48-byte IPv6 packet being represented as a UDP payload beyond the packet with a very large payload length. The validation adjustment can also raise the reported captured length to 64.
## Files
- `poc/ipv6_extlen_wrap_probe.cpp` - standalone C++17 parser-behavior PoC with no source comments
- `evidence/2026-06-23-local-harness-output.txt` - local harness output
- `docs/research-inventory.md` - additional Nmap vulnerability research inventory from the same push
## Affected source path
The core parser behavior is in:
```text
libnetutil/netutil.cc:688-712
```
The validation adjustment path is in:
```text
tcpip.cc:1312-1323
```
Relevant downstream consumers include:
```text
scan_engine_raw.cc:1690-1812
scan_engine_raw.cc:1941-2008
scan_engine_raw.cc:2087-2129
```
## Build and run
Linux, macOS, WSL, or MinGW:
```bash
g++ -std=c++17 -O0 -g -Wall -Wextra -o ipv6_extlen_wrap_probe poc/ipv6_extlen_wrap_probe.cpp
./ipv6_extlen_wrap_probe
```
Windows PowerShell with MinGW:
```powershell
g++ -std=c++17 -O0 -g -Wall -Wextra -o ipv6_extlen_wrap_probe.exe .\poc\ipv6_extlen_wrap_probe.cpp
.\ipv6_extlen_wrap_probe.exe
```
Expected output:
```text
helper_returned=true
next_header=17
payload_offset=56
wrapped_payload_len=4294967288
validator_len_after_adjust=64
captured_len=48
```
## Mechanics
The helper path advances through IPv6 extension headers with this shape:
```text
p += (extension_length + 1) * 8
```
The missing invariant is a post-advance containment check before the remaining length is computed. If the declared extension length moves `p` beyond the packet end, the remaining length calculation wraps when stored in `unsigned int`.
After that, raw scan and packet validation consumers can treat the packet as if it still has a valid upper-layer payload.
## Research notes
This entry is focused on the IPv6 extension-header length wrap because it is the strongest fresh parser candidate from the latest Nmap review pass.
Other reviewed surfaces from the same research push are captured in `docs/research-inventory.md`, including raw scan short transport-header reads, Nping EchoClient NEP partial-packet handling, Ncat HTTP parser issues, and NSE negative-length behavior.
Exploitability research is ongoing around whether this parser primitive can be shaped into a stronger memory primitive beyond parser-state corruption, out-of-bounds reads, and fatal allocation behavior.
## Responsible use
Run the PoC only in a local research environment. The included PoC is a standalone arithmetic and parser-behavior harness for studying the parser transition.

View File

@@ -0,0 +1,127 @@
# Nmap research inventory
Research status: ongoing.
This inventory captures the strongest candidates from the local Nmap review pass that produced the IPv6 extension-header length wrap PoC.
## IPv6 extension-header length wrap
Primary package candidate.
Affected path:
```text
libnetutil/netutil.cc:688-712
tcpip.cc:1312-1323
scan_engine_raw.cc:1690-1812
scan_engine_raw.cc:1941-2008
scan_engine_raw.cc:2087-2129
```
Remote IPv6 packet input can move the parser payload pointer beyond the captured packet and wrap the remaining payload length to a large unsigned value. Local harness validation shows a 48-byte packet yielding payload offset 56 and payload length `4294967288`.
Current observed behavior class:
- parser-state corruption
- out-of-bounds read paths in raw scan consumers
- fatal allocation behavior when wrapped lengths reach early UDP response storage
## Raw scan short transport-header reads
Affected path:
```text
scan_engine_raw.cc
```
Several raw scan paths copy transport headers from packet buffers after checks that allow shorter-than-copied protocol headers. The strongest cases are direct SCTP parsing, ICMP quoted-packet parsing, and encapsulated TCP/SCTP parsing. These paths are reachable from remote packet responses during raw scan modes.
Observed behavior class:
- remotely triggerable scanner crash
- out-of-bounds read from captured packet buffers
- scan-state misclassification opportunities during malformed response handling
## Nping EchoClient NEP partial-packet state
Affected path:
```text
nping/EchoClient.cc:790-864
nping/EchoClient.cc:874-940
```
The EchoClient NEP receive path retains a partial packet buffer and later combines it with new bytes based on a decrypted packet length. Suspicious state transitions include inconsistent accounting between retained bytes and new bytes, plus copies based on packet lengths derived from the encrypted stream after the user has connected to an EchoServer.
Observed behavior class:
- hostile EchoServer-triggered EchoClient crash
- negative-size and oversized-copy behavior in partial-packet transitions
- ongoing exploitability research around partial-state shaping
## Ncat HTTP proxy parser issues
Affected path:
```text
ncat/http.c
ncat/ncat_proxy.c
```
The Ncat HTTP proxy surface contains parser behaviors around embedded NUL bytes and malformed status/request lines. Existing local research reproduced loop and out-of-bounds read style behavior under malformed HTTP proxy traffic.
Observed behavior class:
- unauthenticated proxy-side denial of service
- parser desynchronization around embedded NUL handling
- status-line out-of-bounds read behavior
## Nping EchoServer stale context read
Affected path:
```text
nping/EchoServer.cc
```
EchoServer session handling has a stale-context read candidate during connection/session transition behavior. This is grouped with the Nping NEP findings because both involve Echo protocol state and attacker-controlled peer behavior.
Observed behavior class:
- server-side crash candidate
- stale session/context read during crafted peer interaction
## NSE negative-length receive behavior
Affected path:
```text
nse_nsock.cc:778-788
liblua/lapi.c:526-534
liblua/lstring.c:222-230
```
Several NSE protocol libraries compute receive lengths from remote protocol length fields. Negative script-level lengths can cross into buffered receive and Lua string construction behavior. Lua's string allocator rejects oversized string lengths before copying in the tested path.
Observed behavior class:
- script-level protocol parser failure
- allocation error path through Lua string handling
- ongoing review target for script-specific length guards
## Execution-surface survey
Reviewed code execution and code-loading surfaces include:
```text
ncat/ncat_exec_win.c
ncat/ncat_posix.c
ncat/ncat_lua.c
nselib/data/psexec/nmap_service.c
scripts/*-exec.nse
scripts/smb-psexec.nse
scripts/membase-http-info.nse
libpcap/pcap.c
```
The survey is kept in scope because the broader research goal includes execution-path discovery. The practical candidates currently remain concentrated in packet parsing and protocol state handling.

View File

@@ -0,0 +1,6 @@
helper_returned=true
next_header=17
payload_offset=56
wrapped_payload_len=4294967288
validator_len_after_adjust=64
captured_len=48

View File

@@ -0,0 +1,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;
}