Skip to content

Possible out-of-bounds read: ndpi_processPacket trusts an unchecked pcap_pkthdr cast and attacker-controlled header->len #4

Description

@OvOhao

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_processPacketpcap_packet_callbackpacket_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
  }
}
  1. 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.
  2. 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.
  3. ethernet->h_proto (offset 12) is read before any check that packet has ≥14 bytes.
  4. 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.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions