Possible out-of-bounds read: ndpi_processPacket trusts an unchecked pcap_pkthdr cast and attacker-controlled header->len
I found a possible OOB read in ndpi_processPacket. The method casts its first Buffer argument
directly to struct pcap_pkthdr * with no size check, takes the packet as a second Buffer whose
real length is discarded, and then dissects the packet using header->len /
header->caplen — values read out of the first (attacker-controlled) Buffer — as the packet
size. Nothing ever compares those lengths against the actual size of the packet Buffer, so both
the header-struct read and the IP/L7 dissection can read far past the end of the JS Buffers.
The binding is exported to JS as processPacket, so a caller parsing an untrusted capture whose
incl_len/len field exceeds the captured bytes reaches this directly.
File: ndpiexlib.c
Function: ndpi_processPacket → pcap_packet_callback → packet_processing
NAPI_METHOD(ndpi_processPacket) {
NAPI_ARGV(2);
NAPI_ARGV_BUFFER_CAST(struct pcap_pkthdr *, header, 0) // no size check on buffer 0
NAPI_ARGV_BUFFER(packet, 1) // packet_len captured but ignored
processPacket(header,packet);
return NULL;
}
static void pcap_packet_callback(u_char * args, const struct pcap_pkthdr *header, const char * packet) {
const struct ethhdr *ethernet = (struct ethhdr *) packet;
struct iphdr *iph = (struct iphdr *) &packet[sizeof(struct ethhdr)];
...
type = ethernet->h_proto; // reads packet[12..13] with no length check
if (type == htons(ETH_P_IP) && header->caplen >= sizeof(struct ethhdr)) {
if(header->caplen < header->len) { fprintf(stderr, "WARNING ..."); sleep(2); } // warns, continues
if(iph->version != 4) { ...; return; }
packet_processing(time, iph, header->len - sizeof(struct ethhdr), header->len); // ipsize from header->len
}
}
NAPI_ARGV_BUFFER_CAST(struct pcap_pkthdr *, header, 0) casts the raw bytes of Buffer 0 to
struct pcap_pkthdr *; the captured header_len is never checked, so a Buffer smaller than
sizeof(struct pcap_pkthdr) makes every header->... access out of bounds.
NAPI_ARGV_BUFFER(packet, 1) yields packet and packet_len, but packet_len is never used
— the real bound on how many bytes may be read from packet is discarded.
ethernet->h_proto (offset 12) is read before any check that packet has ≥14 bytes.
- The only guard,
header->caplen >= sizeof(struct ethhdr), uses the attacker-controlled
caplen from Buffer 0, not the true packet_len, so it is trivially satisfied while packet
stays tiny. header->caplen < header->len merely prints a warning and continues.
packet_processing(time, iph, header->len - sizeof(struct ethhdr), header->len) passes
ipsize = header->len - 14 (attacker-controlled) into ndpi_detection_process_packet, which
reads up to ipsize/iph->tot_len bytes from iph — well past the end of the packet
Buffer. Out-of-bounds read.
JS trigger (if applicable):
const ndpi = require('node-ndpi');
ndpi.init();
const packet = Buffer.alloc(16); // tiny packet
const header = Buffer.alloc(64); // struct pcap_pkthdr
// craft ts + caplen + len so caplen>=14 and len is huge:
header.writeUInt32LE(0x40000000, 16); // len field (offset is layout-dependent) -> ~1GB
ndpi.processPacket(header, packet); // dissects ~1GB from a 16-byte buffer -> OOB read
Suggested fix: validate header_len >= sizeof(struct pcap_pkthdr) before dereferencing the
header, and bound the dissection by the real packet Buffer length — clamp
caplen/len to packet_len (and require packet_len >= sizeof(struct ethhdr)) so
ndpi_detection_process_packet can never be told to read more bytes than the Buffer holds.
Possible out-of-bounds read:
ndpi_processPackettrusts an uncheckedpcap_pkthdrcast and attacker-controlledheader->lenI found a possible OOB read in
ndpi_processPacket. The method casts its first Buffer argumentdirectly to
struct pcap_pkthdr *with no size check, takes the packet as a second Buffer whosereal length is discarded, and then dissects the packet using
header->len/header->caplen— values read out of the first (attacker-controlled) Buffer — as the packetsize. Nothing ever compares those lengths against the actual size of the packet Buffer, so both
the header-struct read and the IP/L7 dissection can read far past the end of the JS Buffers.
The binding is exported to JS as
processPacket, so a caller parsing an untrusted capture whoseincl_len/lenfield exceeds the captured bytes reaches this directly.File:
ndpiexlib.cFunction:
ndpi_processPacket→pcap_packet_callback→packet_processingNAPI_ARGV_BUFFER_CAST(struct pcap_pkthdr *, header, 0)casts the raw bytes of Buffer 0 tostruct pcap_pkthdr *; the capturedheader_lenis never checked, so a Buffer smaller thansizeof(struct pcap_pkthdr)makes everyheader->...access out of bounds.NAPI_ARGV_BUFFER(packet, 1)yieldspacketandpacket_len, butpacket_lenis never used— the real bound on how many bytes may be read from
packetis discarded.ethernet->h_proto(offset 12) is read before any check thatpackethas ≥14 bytes.header->caplen >= sizeof(struct ethhdr), uses the attacker-controlledcaplenfrom Buffer 0, not the truepacket_len, so it is trivially satisfied whilepacketstays tiny.
header->caplen < header->lenmerely prints a warning and continues.packet_processing(time, iph, header->len - sizeof(struct ethhdr), header->len)passesipsize = header->len - 14(attacker-controlled) intondpi_detection_process_packet, whichreads up to
ipsize/iph->tot_lenbytes fromiph— well past the end of thepacketBuffer. Out-of-bounds read.
JS trigger (if applicable):
Suggested fix: validate
header_len >= sizeof(struct pcap_pkthdr)before dereferencing theheader, and bound the dissection by the real packet Buffer length — clamp
caplen/lentopacket_len(and requirepacket_len >= sizeof(struct ethhdr)) sondpi_detection_process_packetcan never be told to read more bytes than the Buffer holds.