diff options
author | Ivan Nardi <12729895+IvanNardi@users.noreply.github.com> | 2021-03-02 21:15:40 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-02 21:15:40 +0100 |
commit | 4c00ff89dfa64f1026c2f1d267dc081a86b45243 (patch) | |
tree | 42a219182bf7d40a7ab17fb9c856d4ab22432657 | |
parent | 2b71a329c18026cb56d938db6f14014aaf682abd (diff) |
DTLS: improve support (#1146)
* DTLS: add some pcap tests
* DTLS: fix parsing of Client/Server Helllo message
* DTLS: add parsing of server certificates
-rw-r--r-- | src/lib/ndpi_main.c | 1 | ||||
-rw-r--r-- | src/lib/ndpi_utils.c | 1 | ||||
-rw-r--r-- | src/lib/protocols/tls.c | 139 | ||||
-rw-r--r-- | tests/pcap/dtls2.pcap | bin | 0 -> 5495 bytes | |||
-rw-r--r-- | tests/pcap/dtls_certificate_fragments.pcap | bin | 0 -> 6322 bytes | |||
-rw-r--r-- | tests/pcap/dtls_session_id_and_coockie_both.pcap | bin | 0 -> 692 bytes | |||
-rw-r--r-- | tests/result/dtls2.pcap.out | 8 | ||||
-rw-r--r-- | tests/result/dtls_certificate_fragments.pcap.out | 8 | ||||
-rw-r--r-- | tests/result/dtls_session_id_and_coockie_both.pcap.out | 8 |
9 files changed, 127 insertions, 38 deletions
diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index 5afdc4695..5d23ec514 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -6972,6 +6972,7 @@ uint8_t ndpi_connection_tracking(struct ndpi_detection_module_struct *ndpi_str, switch(proto) { case NDPI_PROTOCOL_TLS: + case NDPI_PROTOCOL_DTLS: if(flow->l4.tcp.tls.certificate_processed) return(0); if(flow->l4.tcp.tls.num_tls_blocks <= ndpi_str->num_tls_blocks_to_follow) { diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index d50e8ccae..75f7b903d 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -1290,6 +1290,7 @@ int ndpi_dpi2json(struct ndpi_detection_module_struct *ndpi_struct, break; case NDPI_PROTOCOL_TLS: + case NDPI_PROTOCOL_DTLS: if(flow->protos.tls_quic_stun.tls_quic.ssl_version) { char notBefore[32], notAfter[32]; struct tm a, b, *before = NULL, *after = NULL; diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index 5e3ae65b1..f9d96f55c 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -546,8 +546,9 @@ static void processCertificateElements(struct ndpi_detection_module_struct *ndpi int processCertificate(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; + int is_dtls = packet->udp ? 1 : 0; u_int32_t certificates_length, length = (packet->payload[1] << 16) + (packet->payload[2] << 8) + packet->payload[3]; - u_int16_t certificates_offset = 7; + u_int32_t certificates_offset = 7 + (is_dtls ? 8 : 0); u_int8_t num_certificates_found = 0; SHA1_CTX srv_cert_fingerprint_ctx ; @@ -559,14 +560,16 @@ int processCertificate(struct ndpi_detection_module_struct *ndpi_struct, packet->payload[3], packet->payload[4], packet->payload[5]); #endif - if((packet->payload_packet_len != (length + 4)) || (packet->payload[1] != 0x0)) { + if((packet->payload_packet_len != (length + 4 + (is_dtls ? 8 : 0))) || (packet->payload[1] != 0x0)) { NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET); return(-1); /* Invalid length */ } - certificates_length = (packet->payload[4] << 16) + (packet->payload[5] << 8) + packet->payload[6]; + certificates_length = (packet->payload[certificates_offset - 3] << 16) + + (packet->payload[certificates_offset - 2] << 8) + + packet->payload[certificates_offset - 1]; - if((packet->payload[4] != 0x0) || ((certificates_length+3) != length)) { + if((packet->payload[certificates_offset - 3] != 0x0) || ((certificates_length+3) != length)) { NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET); return(-2); /* Invalid length */ } @@ -578,7 +581,7 @@ int processCertificate(struct ndpi_detection_module_struct *ndpi_struct, /* Invalid lenght */ if((certificate_len == 0) || (packet->payload[certificates_offset] != 0x0) - || ((certificates_offset+certificate_len) > (4+certificates_length))) { + || ((certificates_offset+certificate_len) > (4+certificates_length+(is_dtls ? 8 : 0)))) { #ifdef DEBUG_TLS printf("[TLS] Invalid length [certificate_len: %u][certificates_offset: %u][%u vs %u]\n", certificate_len, certificates_offset, @@ -667,6 +670,7 @@ int processCertificate(struct ndpi_detection_module_struct *ndpi_struct, static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; + int ret; switch(packet->payload[0] /* block type */) { case 0x01: /* Client Hello */ @@ -691,7 +695,12 @@ static int processTLSBlock(struct ndpi_detection_module_struct *ndpi_struct, /* Important: populate the tls union fields only after * ndpi_int_tls_add_connection has been called */ if(flow->l4.tcp.tls.hello_processed) { - processCertificate(ndpi_struct, flow); + ret = processCertificate(ndpi_struct, flow); + if (ret != 1) { +#ifdef DEBUG_TLS + printf("[TLS] Error processing certificate: %d\n", ret); +#endif + } flow->l4.tcp.tls.certificate_processed = 1; } break; @@ -861,50 +870,97 @@ static int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct, static int ndpi_search_tls_udp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; - // u_int8_t handshake_type; u_int32_t handshake_len; - u_int16_t p_len; + u_int16_t p_len, processed; const u_int8_t *p; + u_int8_t no_dtls = 0, change_cipher_found = 0; #ifdef DEBUG_TLS printf("[TLS] %s()\n", __FUNCTION__); #endif - /* Consider only specific SSL packets (handshake) */ - if((packet->payload_packet_len < 17) - || (packet->payload[0] != 0x16) - || (packet->payload[1] != 0xfe) /* We ignore old DTLS versions */ - || ((packet->payload[2] != 0xff) && (packet->payload[2] != 0xfd)) - || ((ntohs(*((u_int16_t*)&packet->payload[11]))+13) != packet->payload_packet_len) - ) { - no_dtls: + /* Overwriting packet payload */ + p = packet->payload, p_len = packet->payload_packet_len; /* Backup */ + + /* Split the element in blocks */ + processed = 0; + while(processed + 13 < p_len) { + u_int32_t block_len; + const u_int8_t *block = (const u_int8_t *)&p[processed]; + if((block[0] != 0x16 && block[0] != 0x14) || /* Handshake, change-cipher-spec */ + (block[1] != 0xfe) || /* We ignore old DTLS versions */ + ((block[2] != 0xff) && (block[2] != 0xfd))) { +#ifdef DEBUG_TLS + printf("[TLS] DTLS invalid block 0x%x or old version 0x%x-0x%x-0x%x\n", + block[0], block[1], block[2], block[3]); +#endif + no_dtls = 1; + break; + } + block_len = ntohs(*((u_int16_t*)&block[11])); +#ifdef DEBUG_TLS + printf("[TLS] DTLS block len: %d\n", block_len); +#endif + if (block_len == 0 || (processed + block_len + 12 >= p_len)) { +#ifdef DEBUG_TLS + printf("[TLS] DTLS invalid block len %d (processed %d, p_len %d)\n", + block_len, processed, p_len); +#endif + no_dtls = 1; + break; + } + /* We process only handshake msgs */ + if(block[0] == 0x16) { + if (processed + block_len + 13 > p_len) { +#ifdef DEBUG_TLS + printf("[TLS] DTLS invalid len %d %d %d\n", processed, block_len, p_len); +#endif + no_dtls = 1; + break; + } + /* TODO: handle (certificate) fragments */ + handshake_len = (block[14] << 16) + (block[15] << 8) + block[16]; + if((handshake_len + 12) != block_len) { +#ifdef DEBUG_TLS + printf("[TLS] DTLS invalid handshake_len %d, %d)\n", + handshake_len, block_len); +#endif + no_dtls = 1; + break; + } + packet->payload = &block[13]; + packet->payload_packet_len = block_len; + processTLSBlock(ndpi_struct, flow); + } else { + /* Change-cipher-spec: any subsequent block might be encrypted */ #ifdef DEBUG_TLS - printf("[TLS] No DTLS found\n"); + printf("[TLS] Change-cipher-spec\n"); #endif + change_cipher_found = 1; + processed += block_len + 13; + break; + } - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); - return(0); /* Giveup */ + processed += block_len + 13; + } + if(processed != p_len) { +#ifdef DEBUG_TLS + printf("[TLS] DTLS invalid processed len %d/%d (%d)\n", processed, p_len, change_cipher_found); +#endif + if(!change_cipher_found) + no_dtls = 1; } - - // handshake_type = packet->payload[13]; - handshake_len = (packet->payload[14] << 16) + (packet->payload[15] << 8) + packet->payload[16]; - - if((handshake_len+25) != packet->payload_packet_len) - goto no_dtls; - - /* Overwriting packet payload */ - p = packet->payload, p_len = packet->payload_packet_len; /* Backup */ - packet->payload = &packet->payload[13], packet->payload_packet_len -= 13; - - processTLSBlock(ndpi_struct, flow); packet->payload = p; packet->payload_packet_len = p_len; /* Restore */ - ndpi_int_tls_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_DTLS); - - return(1); /* Keep working */ + if(no_dtls || change_cipher_found) { + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + return(0); /* That's all */ + } else { + return(1); /* Keep working */ + } } /* **************************************** */ @@ -1023,7 +1079,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, https://networkengineering.stackexchange.com/questions/55752/why-does-wireshark-show-version-tls-1-2-here-instead-of-tls-1-3 */ if(packet->udp) - offset += 1; + offset += session_id_len + 1; else { if(tls_version < 0x7F15 /* TLS 1.3 lacks of session id */) offset += session_id_len+1; @@ -1127,6 +1183,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif } else if(handshake_type == 0x01 /* Client Hello */) { u_int16_t cipher_len, cipher_offset; + u_int8_t cookie_len = 0; if((session_id_len+base_offset+3) > packet->payload_packet_len) return(0); /* Not found */ @@ -1135,8 +1192,14 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, cipher_len = packet->payload[session_id_len+base_offset+2] + (packet->payload[session_id_len+base_offset+1] << 8); cipher_offset = base_offset + session_id_len + 3; } else { - cipher_len = ntohs(*((u_int16_t*)&packet->payload[base_offset+2])); - cipher_offset = base_offset+4; + cookie_len = packet->payload[base_offset+session_id_len+1]; +#ifdef DEBUG_TLS + printf("[JA3] Client: DTLS cookie len %d\n", cookie_len); +#endif + if((session_id_len+base_offset+cookie_len+4) > packet->payload_packet_len) + return(0); /* Not found */ + cipher_len = ntohs(*((u_int16_t*)&packet->payload[base_offset+session_id_len+cookie_len+2])); + cipher_offset = base_offset + session_id_len + cookie_len + 4; } #ifdef DEBUG_TLS @@ -1175,7 +1238,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif } - offset = base_offset + session_id_len + cipher_len + 2; + offset = base_offset + session_id_len + cookie_len + cipher_len + 2; if(offset < total_len) { u_int16_t compression_len; diff --git a/tests/pcap/dtls2.pcap b/tests/pcap/dtls2.pcap Binary files differnew file mode 100644 index 000000000..68be6c38d --- /dev/null +++ b/tests/pcap/dtls2.pcap diff --git a/tests/pcap/dtls_certificate_fragments.pcap b/tests/pcap/dtls_certificate_fragments.pcap Binary files differnew file mode 100644 index 000000000..5551b5cab --- /dev/null +++ b/tests/pcap/dtls_certificate_fragments.pcap diff --git a/tests/pcap/dtls_session_id_and_coockie_both.pcap b/tests/pcap/dtls_session_id_and_coockie_both.pcap Binary files differnew file mode 100644 index 000000000..932f96e44 --- /dev/null +++ b/tests/pcap/dtls_session_id_and_coockie_both.pcap diff --git a/tests/result/dtls2.pcap.out b/tests/result/dtls2.pcap.out new file mode 100644 index 000000000..cf26153f5 --- /dev/null +++ b/tests/result/dtls2.pcap.out @@ -0,0 +1,8 @@ +DTLS 30 4991 1 + +JA3 Host Stats: + IP Address # JA3C + 1 61.68.110.153 1 + + + 1 UDP 61.68.110.153:53045 <-> 212.32.214.39:61457 [proto: 30/DTLS][cat: Web/5][14 pkts/2246 bytes <-> 16 pkts/2745 bytes][Goodput ratio: 74/75][382.15 sec][bytes ratio: -0.100 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 2/241 27857/28359 60550/60551 26256/25033][Pkt Len c2s/s2c min/avg/max/stddev: 123/102 160/172 325/867 46/180][Risk: ** Weak TLS cipher **** TLS (probably) not carrying HTTPS **** SNI TLS extension was missing **][DTLSv1.0][JA3C: 1b45c913a0c0fde5f263502e65999485][JA3S: 749bd1edea60396ffaa65213b7971718 (WEAK)][Issuer: C=US][Subject: C=US, CN=*.relay.ros.rockstargames.com][Validity: 2014-09-12 21:31:19 - 2037-02-15 21:31:19][Cipher: TLS_RSA_WITH_AES_256_CBC_SHA][PLAIN TEXT (140912213119Z)][Plen Bins: 0,3,43,46,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] diff --git a/tests/result/dtls_certificate_fragments.pcap.out b/tests/result/dtls_certificate_fragments.pcap.out new file mode 100644 index 000000000..e9461e6c9 --- /dev/null +++ b/tests/result/dtls_certificate_fragments.pcap.out @@ -0,0 +1,8 @@ +DTLS 20 5978 1 + +JA3 Host Stats: + IP Address # JA3C + 1 10.186.198.149 1 + + + 1 UDP 10.186.198.149:39347 <-> 35.210.59.134:44443 [proto: 30/DTLS][cat: Web/5][11 pkts/2624 bytes <-> 9 pkts/3354 bytes][Goodput ratio: 82/89][2.92 sec][bytes ratio: -0.122 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 324/76 2179/186 659/75][Pkt Len c2s/s2c min/avg/max/stddev: 167/90 239/373 416/1454 97/388][Risk: ** Weak TLS cipher **** TLS (probably) not carrying HTTPS **** SNI TLS extension was missing **][DTLSv1.0][JA3C: 3c3d129780d0066cd8936a6291a8d44f][JA3S: d45798bc098cd930de7eb2f5f866e994 (WEAK)][Cipher: TLS_RSA_WITH_AES_256_CBC_SHA][PLAIN TEXT (Opera Software ASA1)][Plen Bins: 0,5,0,35,5,10,10,0,10,10,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0] diff --git a/tests/result/dtls_session_id_and_coockie_both.pcap.out b/tests/result/dtls_session_id_and_coockie_both.pcap.out new file mode 100644 index 000000000..d729967ea --- /dev/null +++ b/tests/result/dtls_session_id_and_coockie_both.pcap.out @@ -0,0 +1,8 @@ +DTLS 4 604 1 + +JA3 Host Stats: + IP Address # JA3C + 1 185.196.113.239 1 + + + 1 UDP 185.196.113.239:50257 <-> 223.116.105.247:44443 [proto: 30/DTLS][cat: Web/5][2 pkts/302 bytes <-> 2 pkts/302 bytes][Goodput ratio: 72/72][0.06 sec][Risk: ** TLS (probably) not carrying HTTPS **** SNI TLS extension was missing **][DTLSv1.2][JA3C: e15c510766789ed8f49de0e37951c1da][JA3S: a1d48eca741e476d8ee735578a26bdbd][Cipher: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384][Plen Bins: 0,25,0,50,0,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] |