diff options
-rw-r--r-- | fuzz/fuzz_config.cpp | 20 | ||||
-rw-r--r-- | src/lib/ndpi_utils.c | 1 | ||||
-rw-r--r-- | src/lib/third_party/src/fuzz/pl7m.c | 186 |
3 files changed, 203 insertions, 4 deletions
diff --git a/fuzz/fuzz_config.cpp b/fuzz/fuzz_config.cpp index 647ee4a7b..fe067aa5f 100644 --- a/fuzz/fuzz_config.cpp +++ b/fuzz/fuzz_config.cpp @@ -20,8 +20,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct ndpi_patricia_tree_stats patricia_stats; struct ndpi_automa_stats automa_stats; int cat, idx; - u_int16_t pid; - char *protoname; + u_int16_t pid, pid2; + char *protoname, *protoname2; + char pids_name[32]; const char *name; char catname[] = "name"; struct ndpi_flow_input_info input_info; @@ -486,7 +487,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { pid = fuzzed_data.ConsumeIntegralInRange<u_int16_t>(0, NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1); /* + 1 to trigger invalid pid */ protoname = ndpi_get_proto_by_id(ndpi_info_mod, pid); if (protoname) { - assert(ndpi_get_proto_by_name(ndpi_info_mod, protoname) == pid); + ndpi_get_proto_by_name(ndpi_info_mod, protoname); + + pid2 = fuzzed_data.ConsumeIntegralInRange<u_int16_t>(0, NDPI_MAX_SUPPORTED_PROTOCOLS + NDPI_MAX_NUM_CUSTOM_PROTOCOLS + 1); /* + 1 to trigger invalid pid */ + protoname2 = ndpi_get_proto_by_id(ndpi_info_mod, pid2); + if(protoname2) { + snprintf(pids_name, sizeof(pids_name), "%s.%s", protoname, protoname2); + pids_name[sizeof(pids_name) - 1] = '\0'; + ndpi_get_protocol_by_name(ndpi_info_mod, pids_name); + } } ndpi_map_user_proto_id_to_ndpi_id(ndpi_info_mod, pid); ndpi_map_ndpi_id_to_user_proto_id(ndpi_info_mod, pid); @@ -549,6 +558,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ndpi_get_ip_proto_name(fuzzed_data.ConsumeIntegral<u_int8_t>(), NULL, 0); } ndpi_risk2str(static_cast<ndpi_risk_enum>(fuzzed_data.ConsumeIntegral<u_int64_t>())); + ndpi_risk2code(static_cast<ndpi_risk_enum>(fuzzed_data.ConsumeIntegral<u_int64_t>())); + ndpi_code2risk(ndpi_risk2code(static_cast<ndpi_risk_enum>(fuzzed_data.ConsumeIntegralInRange(0, NDPI_MAX_RISK + 1)))); ndpi_severity2str(static_cast<ndpi_risk_severity>(fuzzed_data.ConsumeIntegral<u_int8_t>())); ndpi_risk2score(static_cast<ndpi_risk_enum>(fuzzed_data.ConsumeIntegral<u_int64_t>()), &unused1, &unused2); ndpi_http_method2str(static_cast<ndpi_http_method>(fuzzed_data.ConsumeIntegral<u_int8_t>())); @@ -619,6 +630,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { { NDPI_PARAM_ISSUER_DN, (void *)("CN=813845657003339838, O=Code42, OU=TEST, ST=MN, C=US") /* from example/protos.txt */}, { NDPI_PARAM_HOST_IPV4, &flow.c_address.v4} }; ndpi_check_flow_risk_exceptions(ndpi_info_mod, 3, params); + + ndpi_risk_params params2[] = { { NDPI_MAX_RISK_PARAM_ID, &flow.c_address.v4} }; /* Invalid */ + ndpi_check_flow_risk_exceptions(ndpi_info_mod, 1, params2); } /* TODO: stub for geo stuff */ ndpi_get_geoip_asn(ndpi_info_mod, NULL, NULL); diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index 69129fa3e..dfe6cf1f5 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -2473,7 +2473,6 @@ ndpi_http_method ndpi_http_str2method(const char* method, u_int16_t method_len) return(NDPI_HTTP_METHOD_MOVE); else return(NDPI_HTTP_METHOD_MKCOL); - break; case 'P': switch(method[1]) { diff --git a/src/lib/third_party/src/fuzz/pl7m.c b/src/lib/third_party/src/fuzz/pl7m.c index bf502b642..827e538e7 100644 --- a/src/lib/third_party/src/fuzz/pl7m.c +++ b/src/lib/third_party/src/fuzz/pl7m.c @@ -159,6 +159,39 @@ struct gre_header { __u16 protocol; }; +#define GTP_MSG_TPDU 0xFF + +struct gtp_header { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u_int16_t n_pdu:1, + sequence:1, + extension:1, + reserved:1, + protocol:1, + version:3, + type:8; +#elif defined(__BIG_ENDIAN_BITFIELD) + u_int16_t version:3, + protocol:1, + reserved:1, + extension:1, + sequence:1, + n_pdu:1, + type:8; +#else +#error "Adjust your <asm/byteorder.h> defines" +#endif + u_int16_t total_length; + u_int32_t teid; +}; + +struct gtp_header_optional { + u_int16_t sn; + u_int8_t n_pdu_nbr; + u_int8_t next_hdr; +}; + + struct m_pkt { unsigned char *raw_data; struct pcap_pkthdr header; @@ -166,6 +199,7 @@ struct m_pkt { int l2_offset; int prev_l3_offset; u_int16_t prev_l3_proto; + int gtp_offset; int l3_offset; u_int16_t l3_proto; int l4_offset; @@ -298,9 +332,13 @@ static int dissect_l2(int datalink_type, struct m_pkt *p) case DLT_PPP: case DLT_C_HDLC: if (data[l2_offset + 0] == 0x0f || data[l2_offset + 0] == 0x8f) { + if (data_len < l2_offset + 4) + return -1; l3_offset = 4; l3_proto = ntohs(*((u_int16_t *)&data[l2_offset + 2])); } else { + if (data_len < l2_offset + 2) + return -1; l3_offset = l2_offset + 2; next = ntohs(*((u_int16_t *)&data[l2_offset + 0])); switch (next) { @@ -704,6 +742,136 @@ static int dissect_l4(struct m_pkt *p) } return 0; } +static int is_gtp_u(unsigned char *gtp_buffer, int gtp_buffer_len, + const struct m_pkt *p, u_int16_t *l3_proto) +{ + struct gtp_header *gtp_h; + struct udphdr *udp_h; + uint16_t new_layer_len = 0; + unsigned char sub_proto; + + if (p->l4_proto != IPPROTO_UDP || + gtp_buffer_len < (int)sizeof(struct gtp_header)) + return 0; + + /* Only default port */ + udp_h = (struct udphdr *)(p->raw_data + p->l4_offset); + if(udp_h->source != htons(2152) && + udp_h->dest != htons(2152)) + return 0; + + gtp_h = (struct gtp_header *)gtp_buffer; + + if (gtp_h->version != 1 || + gtp_h->type != GTP_MSG_TPDU || + gtp_h->reserved != 0 || + ntohs(gtp_h->total_length) > (gtp_buffer_len - sizeof(struct gtp_header))) { + ddbg("Invalid gtp header: %d, 0x%x, 0x%0x, %d vs %d\n", + gtp_h->version, gtp_h->type, gtp_h->reserved, + ntohs(gtp_h->total_length), gtp_buffer_len); + return 0; + } + + new_layer_len = sizeof(struct gtp_header); + + /* Optional header version 1 */ + if (gtp_h->extension || gtp_h->sequence || gtp_h->n_pdu) { + new_layer_len += sizeof(struct gtp_header_optional); + + if (gtp_buffer_len < new_layer_len) + return 0; + } + if (gtp_h->extension) { + unsigned int length = 0; + + while (new_layer_len < (gtp_buffer_len - 1)) { + length = gtp_buffer[new_layer_len] << 2; + new_layer_len += length; + if (new_layer_len > gtp_buffer_len || + gtp_buffer[new_layer_len - 1] == 0 || length == 0) + break; + } + if (new_layer_len > gtp_buffer_len || + gtp_buffer[new_layer_len - 1] != 0 || + length == 0) { + return 0; + } + } + + /* Trying to detect next proto. Code taken from wireshark */ + if (gtp_buffer_len < new_layer_len + 1) + return 0; + sub_proto = gtp_buffer[new_layer_len]; + if ((sub_proto >= 0x45) && (sub_proto <= 0x4e)) { + /* This is most likely an IPv4 packet + * we can exclude 0x40 - 0x44 because the minimum header size is 20 octets + * 0x4f is excluded because PPP protocol type "IPv6 header compression" + * with protocol field compression is more likely than a plain + * IPv4 packet with 60 octet header size */ + *l3_proto = ETH_P_IP; + } else if ((sub_proto & 0xf0) == 0x60) { + /* This is most likely an IPv6 packet */ + *l3_proto = ETH_P_IPV6; + } else { + /* This seems to be a PPP packet */ + /* TODO: code not back-ported from wireshark yet*/ + return 0; + } + + return new_layer_len; +} +static int dissect_l4_detunneling(struct m_pkt *p) +{ + unsigned char *data = p->raw_data + p->l5_offset; + int data_len = p->header.caplen - p->l5_offset; + u_int16_t next_l3_proto; + int gtp_header_len, rc; + + ddbg("L4(detunel): l4_proto %d data_len %d l5_length %d\n", + p->l4_proto, data_len, p->l5_length); + + if (data_len < 0 || p->l5_length > data_len) + return -1; + + /* TODO: try to handle tunnel over fragment */ + if (p->is_l3_fragment) { + ddbg("Skip L4(detunnel) dissection because it is a fragment\n"); + return 0; + } + /* No reasons to detunnel if we skipped L4 dissection */ + if (p->skip_l4_dissection) { + ddbg("Skip L4 dissection\n"); + return 0; + } + + /* GTP detunneling: looking only for MSG T-PDU that carries + encapsulated data */ + gtp_header_len = is_gtp_u(data, data_len, p, &next_l3_proto); + if (gtp_header_len > 0) { + ddbg("Found GTP-U\n"); + if (p->prev_l3_proto == 0) { + assert(p->prev_l3_offset == 0); + p->prev_l3_proto = p->l3_proto; + p->prev_l3_offset = p->l3_offset; + } else { + derr("Multiple tunnels. Unsupported\n"); + return -1; + } + assert(p->gtp_offset == 0); + p->gtp_offset = p->l5_offset; + p->l3_proto = next_l3_proto; + p->l3_offset = p->l5_offset + gtp_header_len; + rc = dissect_l3(p); + if (rc != 0) { + derr("Error dissect_l3 (after gtp)\n"); + return -1; + } + return dissect_l4(p); + } + + /* "Normal" L4 traffic */ + return 0; +} static int dissect_do(int datalink_type, struct m_pkt *p) { int rc; @@ -723,6 +891,12 @@ static int dissect_do(int datalink_type, struct m_pkt *p) derr("Error dissect_l4\n"); return -1; } + /* Some kind of detunneling over L4 (usually over UDP). Example: GTP */ + rc = dissect_l4_detunneling(p); + if (rc != 0) { + derr("Error dissect_l5\n"); + return -1; + } return 0; } @@ -788,6 +962,7 @@ static void update_do_l7(struct m_pkt *p) { struct udphdr *udp_h; struct tcphdr *tcp_h; + struct gtp_header *gtp_h; size_t new_l5_len; int l4_header_len = 0; int l5_len_diff; @@ -853,6 +1028,16 @@ static void update_do_l7(struct m_pkt *p) ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) + l5_len_diff); } + /* Update GTP header */ + if (p->gtp_offset) { + gtp_h = (struct gtp_header *)(p->raw_data + p->gtp_offset); + gtp_h->total_length = htons(ntohs(gtp_h->total_length) + l5_len_diff); + /* Update previous UDP header */ + assert(p->gtp_offset > (int)sizeof(struct udphdr)); + udp_h = (struct udphdr *)(p->raw_data + p->gtp_offset - sizeof(struct udphdr)); + udp_h->len = htons(ntohs(udp_h->len) + l5_len_diff); + } + p->l5_length = new_l5_len; ddbg("cap_len %u->%zu\n", p->header.caplen, p->l5_offset + new_l5_len); p->header.caplen = p->l5_offset + new_l5_len; @@ -951,6 +1136,7 @@ static struct m_pkt *__dup_pkt(struct m_pkt *p) n->l2_offset = p->l2_offset; n->prev_l3_offset = p->prev_l3_offset; n->prev_l3_proto = p->prev_l3_proto; + n->gtp_offset = p->gtp_offset; n->l4_offset = p->l4_offset; n->l3_offset = p->l3_offset; n->l3_proto = p->l3_proto; |