diff options
author | Luca Deri <deri@ntop.org> | 2023-12-22 20:39:44 +0100 |
---|---|---|
committer | Luca Deri <deri@ntop.org> | 2023-12-22 20:40:42 +0100 |
commit | 8285fffdaeda5d2405360719a57f817b4772e6d1 (patch) | |
tree | 293b33f5c1264f9038988aae9b33e1f0ac7a388c /src/lib/protocols/tls.c | |
parent | b90c18e9069cd5b3cfcda718263b910e949d8b57 (diff) |
Implements JA4 Support (#2191)
Diffstat (limited to 'src/lib/protocols/tls.c')
-rw-r--r-- | src/lib/protocols/tls.c | 486 |
1 files changed, 321 insertions, 165 deletions
diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index 11caaae8a..8949e22e5 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -25,6 +25,7 @@ #include "ndpi_api.h" #include "ndpi_md5.h" #include "ndpi_sha1.h" +#include "ndpi_sha256.h" #include "ndpi_encryption.h" #include "ndpi_private.h" @@ -55,23 +56,25 @@ static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_st */ #define JA_STR_LEN 1024 -#define MAX_NUM_JA 512 +#define MAX_NUM_JA 128 #define MAX_JA_STRLEN 256 union ja_info { struct { u_int16_t tls_handshake_version; - u_int16_t num_cipher, cipher[MAX_NUM_JA]; - u_int16_t num_tls_extension, tls_extension[MAX_NUM_JA]; + u_int16_t num_ciphers, cipher[MAX_NUM_JA]; + u_int16_t num_tls_extensions, tls_extension[MAX_NUM_JA]; u_int16_t num_elliptic_curve, elliptic_curve[MAX_NUM_JA]; u_int16_t num_elliptic_curve_point_format, elliptic_curve_point_format[MAX_NUM_JA]; - char signature_algorithms[MAX_JA_STRLEN], supported_versions[MAX_JA_STRLEN], alpn[MAX_JA_STRLEN]; + u_int16_t num_signature_algorithms, signature_algorithms[MAX_NUM_JA]; + u_int16_t num_supported_versions, supported_versions[MAX_NUM_JA]; + char signature_algorithms_str[MAX_JA_STRLEN], alpn[MAX_JA_STRLEN]; } client; - + struct { u_int16_t tls_handshake_version; - u_int16_t num_cipher, cipher[MAX_NUM_JA]; - u_int16_t num_tls_extension, tls_extension[MAX_NUM_JA]; + u_int16_t num_ciphers, cipher[MAX_NUM_JA]; + u_int16_t num_tls_extensions, tls_extension[MAX_NUM_JA]; u_int16_t tls_supported_version; u_int16_t num_elliptic_curve_point_format, elliptic_curve_point_format[MAX_NUM_JA]; char alpn[MAX_JA_STRLEN]; @@ -521,16 +524,16 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct if(flow->protos.tls_quic.notBefore > TLS_LIMIT_DATE) if((flow->protos.tls_quic.notAfter-flow->protos.tls_quic.notBefore) > TLS_THRESHOLD) { char str[64]; - + snprintf(str, sizeof(str), "TLS Cert lasts %u days", (flow->protos.tls_quic.notAfter-flow->protos.tls_quic.notBefore) / 86400); ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERT_VALIDITY_TOO_LONG, str); /* Certificate validity longer than 13 months */ } - + if((time_sec < flow->protos.tls_quic.notBefore) || (time_sec > flow->protos.tls_quic.notAfter)) { char str[96], b[32], e[32]; - struct tm result; + struct tm result; time_t theTime; theTime = flow->protos.tls_quic.notBefore; @@ -538,13 +541,13 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct theTime = flow->protos.tls_quic.notAfter; strftime(e, sizeof(e), "%d/%b/%Y %H:%M:%S", ndpi_gmtime_r(&theTime, &result)); - + snprintf(str, sizeof(str), "%s - %s", b, e); ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_CERTIFICATE_EXPIRED, str); /* Certificate expired */ } else if((time_sec > flow->protos.tls_quic.notBefore) && (time_sec > (flow->protos.tls_quic.notAfter - (ndpi_struct->tls_certificate_expire_in_x_days * 86400)))) { char str[96], b[32], e[32]; - struct tm result; + struct tm result; time_t theTime; theTime = flow->protos.tls_quic.notBefore; @@ -750,10 +753,10 @@ void processCertificateElements(struct ndpi_detection_module_struct *ndpi_struct /* Last resort: we check if this is a trusted issuerDN */ if(ndpi_check_issuerdn_risk_exception(ndpi_struct, flow->protos.tls_quic.issuerDN)) return; /* This is a trusted DN */ - + ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_SELFSIGNED_CERTIFICATE, flow->protos.tls_quic.subjectDN); } - + #if DEBUG_TLS printf("[TLS] %s() SubjectDN [%s]\n", __FUNCTION__, rdnSeqBuf); #endif @@ -1122,10 +1125,10 @@ static int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct, /* Use positive values for c->s e negative for s->c */ if(packet->packet_direction != 0) blen = -blen; - + flow->l4.tcp.tls.tls_application_blocks_len[flow->l4.tcp.tls.num_tls_blocks++] = blen; } - + #ifdef DEBUG_TLS_BLOCKS printf("*** [TLS Block] [len: %u][num_tls_blocks: %u/%u]\n", len-5, flow->l4.tcp.tls.num_tls_blocks, ndpi_struct->num_tls_blocks_to_follow); @@ -1455,7 +1458,7 @@ static void tlsCheckUncommonALPN(struct ndpi_detection_module_struct *ndpi_struc if(!is_a_common_alpn(ndpi_struct, alpn_start, alpn_len)) { char str[64]; size_t str_len; - + #ifdef DEBUG_TLS printf("TLS uncommon ALPN found: %.*s\n", (int)alpn_len, alpn_start); #endif @@ -1520,17 +1523,17 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, /* see: https://www.wireshark.org/docs/wsar_html/packet-tls-utils_8h_source.html */ static u_int16_t const allowed_non_iana_extensions[] = { 65486 /* ESNI */, 13172 /* NPN - Next Proto Neg */, 17513 /* ALPS */, - 30032 /* Channel ID */, 65445 /* QUIC transport params */, - /* GREASE extensions */ - 2570, 6682, 10794, 14906, 19018, 23130, 27242, - 31354, 35466, 39578, 43690, 47802, 51914, 56026, - 60138, 64250, - /* Groups */ - 1035, 10794, 16696, 23130, 31354, 35466, 51914, - /* Ciphers */ - 102, 129, 52243, 52244, 57363, 65279, 65413, - /* ECH */ - 65037 + 30032 /* Channel ID */, 65445 /* QUIC transport params */, + /* GREASE extensions */ + 2570, 6682, 10794, 14906, 19018, 23130, 27242, + 31354, 35466, 39578, 43690, 47802, 51914, 56026, + 60138, 64250, + /* Groups */ + 1035, 10794, 16696, 23130, 31354, 35466, 51914, + /* Ciphers */ + 102, 129, 52243, 52244, 57363, 65279, 65413, + /* ECH */ + 65037 }; size_t const allowed_non_iana_extensions_size = sizeof(allowed_non_iana_extensions) / sizeof(allowed_non_iana_extensions[0]); @@ -1547,7 +1550,7 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, break; } } - + if(extension_found == 0) { char str[64]; @@ -1566,9 +1569,9 @@ static void checkExtensions(struct ndpi_detection_module_struct *ndpi_struct, if(extension_id == 53 || extension_id == 54) { char str[64]; - + snprintf(str, sizeof(str), "Extn id %u", extension_id); - + #ifdef DEBUG_TLS printf("[TLS] suspicious DTLS-only extension id: %u\n", extension_id); #endif @@ -1590,29 +1593,178 @@ static int check_sni_is_numeric_ip(char *sni) { return 0; } +/* **************************************** */ + +static int u_int16_t_cmpfunc(const void * a, const void * b) { return(*(u_int16_t*)a - *(u_int16_t*)b); } + +/* **************************************** */ + +static void ndpi_compute_ja4(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + u_int32_t quic_version, + union ja_info *ja) { + BYTE tmp_str[JA_STR_LEN]; + u_int tmp_str_len, num_extn; + SHA256_CTX sha_ctx; + BYTE sha_hash[SHA256_BLOCK_SIZE]; + char ja_str[JA_STR_LEN]; + u_int16_t ja_str_len, i; + int rc; + u_int16_t tls_handshake_version = ja->client.tls_handshake_version; + + /* + Compute JA4 TLS/QUIC client + + https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md + + (QUIC=”q” or TCP=”t”) + (2 character TLS version) + (SNI=”d” or no SNI=”i”) + (2 character count of ciphers) + (2 character count of extensions) + (first and last characters of first ALPN extension value) + _ + (sha256 hash of the list of cipher hex codes sorted in hex order, truncated to 12 characters) + _ + (sha256 hash of (the list of extension hex codes sorted in hex order)_(the list of signature algorithms), truncated to 12 characters) + */ + ja_str[0] = (quic_version != 0) ? 'q' : 't'; + + for(i=0; i<ja->client.num_supported_versions; i++) { + if((ja->client.supported_versions[i] != 0x0A0A /* GREASE */) + && (tls_handshake_version < ja->client.supported_versions[i])) + tls_handshake_version = ja->client.supported_versions[i]; + } + + switch(tls_handshake_version) { + case 0x0304: /* TLS 1.3 = “13” */ + ja_str[1] = '1', ja_str[2] = '3'; + break; + + case 0x0303: /* TLS 1.2 = “12” */ + ja_str[1] = '1', ja_str[2] = '2'; + break; + + case 0x0302: /* TLS 1.1 = “11” */ + ja_str[1] = '1', ja_str[2] = '1'; + break; + + case 0x0301: /* TLS 1.0 = “10” */ + ja_str[1] = '1', ja_str[2] = '0'; + break; + + case 0x0300: /* SSL 3.0 = “s3” */ + ja_str[1] = 's', ja_str[2] = '3'; + break; + + case 0x0200: /* SSL 2.0 = “s2” */ + ja_str[1] = 's', ja_str[2] = '2'; + break; + + case 0x0100: /* SSL 1.0 = “s1” */ + ja_str[1] = 's', ja_str[2] = '3'; + break; + + default: + ja_str[1] = '0', ja_str[2] = '0'; + break; + } + + ja_str[3] = ndpi_isset_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST) ? 'i' : 'd', ja_str_len = 4; + + /* JA4_a */ + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%02u%02u%c%c_", + ja->client.num_ciphers, ja->client.num_tls_extensions, + (ja->client.alpn[0] == '\0') ? '0' : ja->client.alpn[0], + (ja->client.alpn[0] == '\0') ? '0' : ja->client.alpn[1]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; + + /* Sort ciphers and extensions */ + qsort(&ja->client.cipher, ja->client.num_ciphers, sizeof(u_int16_t), u_int16_t_cmpfunc); + qsort(&ja->client.tls_extension, ja->client.num_tls_extensions, sizeof(u_int16_t), u_int16_t_cmpfunc); + + tmp_str_len = 0; + for(i=0; i<ja->client.num_ciphers; i++) { + rc = ndpi_snprintf((char *)&tmp_str[tmp_str_len], JA_STR_LEN-tmp_str_len, "%s%04x", + (i > 0) ? "," : "", ja->client.cipher[i]); + if((rc > 0) && (tmp_str_len + rc < JA_STR_LEN)) tmp_str_len += rc; else break; + } + + ndpi_sha256_init(&sha_ctx); + ndpi_sha256_update(&sha_ctx, tmp_str, tmp_str_len); + ndpi_sha256_final(&sha_ctx, sha_hash); + + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, + "%02x%02x%02x%02x%02x%02x_", + sha_hash[0], sha_hash[1], sha_hash[2], + sha_hash[3], sha_hash[4], sha_hash[5]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; + +#ifdef DEBUG_JA + printf("[CIPHER] %s [len: %u]\n", tmp_str, tmp_str_len); +#endif + + tmp_str_len = 0; + for(i=0, num_extn = 0; i<ja->client.num_tls_extensions; i++) { + if((ja->client.tls_extension[i] > 0) && (ja->client.tls_extension[i] != 0x10 /* ALPN extension */)) { + rc = ndpi_snprintf((char *)&tmp_str[tmp_str_len], JA_STR_LEN-tmp_str_len, "%s%04x", + (num_extn > 0) ? "," : "", ja->client.tls_extension[i]); + if((rc > 0) && (tmp_str_len + rc < JA_STR_LEN)) tmp_str_len += rc; else break; + num_extn++; + } + } + + for(i=0; i<ja->client.num_signature_algorithms; i++) { + rc = ndpi_snprintf((char *)&tmp_str[tmp_str_len], JA_STR_LEN-tmp_str_len, "%s%04x", + (i > 0) ? "," : "_", ja->client.signature_algorithms[i]); + if((rc > 0) && (tmp_str_len + rc < JA_STR_LEN)) tmp_str_len += rc; else break; + } + +#ifdef DEBUG_JA + printf("[EXTN] %s [len: %u]\n", tmp_str, tmp_str_len); +#endif + + ndpi_sha256_init(&sha_ctx); + ndpi_sha256_update(&sha_ctx, tmp_str, tmp_str_len); + ndpi_sha256_final(&sha_ctx, sha_hash); + + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, + "%02x%02x%02x%02x%02x%02x", + sha_hash[0], sha_hash[1], sha_hash[2], + sha_hash[3], sha_hash[4], sha_hash[5]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; + +#ifdef DEBUG_JA + printf("[JA4] %s [len: %lu]\n", ja_str, strlen(ja_str)); +#endif + + snprintf(flow->protos.tls_quic.ja4_client, + sizeof(flow->protos.tls_quic.ja4_client), + "%s", ja_str); + +} /* **************************************** */ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, uint32_t quic_version) { + struct ndpi_flow_struct *flow, u_int32_t quic_version) { struct ndpi_packet_struct *packet = &ndpi_struct->packet; - union ja_info ja3; - u_int8_t invalid_ja3 = 0; - u_int16_t tls_version, ja3_str_len; - char ja3_str[JA_STR_LEN]; + union ja_info ja; + u_int8_t invalid_ja = 0; + u_int16_t tls_version, ja_str_len; + char ja_str[JA_STR_LEN]; ndpi_MD5_CTX ctx; u_char md5_hash[16]; u_int32_t i, j; u_int16_t total_len; u_int8_t handshake_type; - int is_quic = (quic_version != 0); - int is_dtls = packet->udp && (!is_quic); + bool is_quic = (quic_version != 0); + bool is_dtls = packet->udp && (!is_quic); #ifdef DEBUG_TLS printf("TLS %s() called\n", __FUNCTION__); #endif - handshake_type = packet->payload[0]; total_len = (packet->payload[1] << 16) + (packet->payload[2] << 8) + packet->payload[3]; @@ -1644,12 +1796,12 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(handshake_type == 0x02 /* Server Hello */) { int rc; - ja3.server.num_cipher = 0; - ja3.server.num_tls_extension = 0; - ja3.server.num_elliptic_curve_point_format = 0; - ja3.server.alpn[0] = '\0'; + ja.server.num_ciphers = 0; + ja.server.num_tls_extensions = 0; + ja.server.num_elliptic_curve_point_format = 0; + ja.server.alpn[0] = '\0'; - ja3.server.tls_handshake_version = tls_version; + ja.server.tls_handshake_version = tls_version; #ifdef DEBUG_TLS printf("TLS Server Hello [version: 0x%04X]\n", tls_version); @@ -1669,19 +1821,19 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if((offset+3) > packet->payload_packet_len) return(0); /* Not found */ - ja3.server.num_cipher = 1, ja3.server.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset])); - if((flow->protos.tls_quic.server_unsafe_cipher = ndpi_is_safe_ssl_cipher(ja3.server.cipher[0])) == 1) { + ja.server.num_ciphers = 1, ja.server.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset])); + if((flow->protos.tls_quic.server_unsafe_cipher = ndpi_is_safe_ssl_cipher(ja.server.cipher[0])) == 1) { char str[64]; char unknown_cipher[8]; - snprintf(str, sizeof(str), "Cipher %s", ndpi_cipher2str(ja3.server.cipher[0], unknown_cipher)); + snprintf(str, sizeof(str), "Cipher %s", ndpi_cipher2str(ja.server.cipher[0], unknown_cipher)); ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_WEAK_CIPHER, str); } - - flow->protos.tls_quic.server_cipher = ja3.server.cipher[0]; + + flow->protos.tls_quic.server_cipher = ja.server.cipher[0]; #ifdef DEBUG_TLS - printf("TLS [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.server.cipher[0]); + printf("TLS [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja.server.cipher[0]); #endif offset += 2 + 1; @@ -1708,8 +1860,8 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, break; } - if(ja3.server.num_tls_extension < MAX_NUM_JA) - ja3.server.tls_extension[ja3.server.num_tls_extension++] = extension_id; + if(ja.server.num_tls_extensions < MAX_NUM_JA) + ja.server.tls_extension[ja.server.num_tls_extensions++] = extension_id; #ifdef DEBUG_TLS printf("TLS [server][extension_id: %u/0x%04X][len: %u]\n", @@ -1725,7 +1877,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, printf("TLS [server] [TLS version: 0x%04X]\n", tls_version); #endif - flow->protos.tls_quic.ssl_version = ja3.server.tls_supported_version = tls_version; + flow->protos.tls_quic.ssl_version = ja.server.tls_supported_version = tls_version; } } else if(extension_id == 16 /* application_layer_protocol_negotiation (ALPN) */ && offset + 6 < packet->payload_packet_len) { @@ -1790,14 +1942,14 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, flow->protos.tls_quic.advertised_alpns == NULL) tlsCheckUncommonALPN(ndpi_struct, flow, flow->protos.tls_quic.negotiated_alpn); - alpn_str_len = ndpi_min(sizeof(ja3.server.alpn), (size_t)alpn_str_len); - memcpy(ja3.server.alpn, alpn_str, alpn_str_len); + alpn_str_len = ndpi_min(sizeof(ja.server.alpn), (size_t)alpn_str_len); + memcpy(ja.server.alpn, alpn_str, alpn_str_len); if(alpn_str_len > 0) - ja3.server.alpn[alpn_str_len - 1] = '\0'; + ja.server.alpn[alpn_str_len - 1] = '\0'; /* Replace , with - as in JA3 */ - for(i=0; ja3.server.alpn[i] != '\0'; i++) - if(ja3.server.alpn[i] == ',') ja3.server.alpn[i] = '-'; + for(i=0; ja.server.alpn[i] != '\0'; i++) + if(ja.server.alpn[i] == ',') ja.server.alpn[i] = '-'; } else if(extension_id == 11 /* ec_point_formats groups */) { u_int16_t s_offset = offset+4 + 1; @@ -1812,17 +1964,17 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, printf("Server TLS [EllipticCurveFormat: %u]\n", s_group); #endif - if(ja3.server.num_elliptic_curve_point_format < MAX_NUM_JA) - ja3.server.elliptic_curve_point_format[ja3.server.num_elliptic_curve_point_format++] = s_group; + if(ja.server.num_elliptic_curve_point_format < MAX_NUM_JA) + ja.server.elliptic_curve_point_format[ja.server.num_elliptic_curve_point_format++] = s_group; else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS - printf("Server TLS Invalid num elliptic %u\n", ja3.server.num_elliptic_curve_point_format); + printf("Server TLS Invalid num elliptic %u\n", ja.server.num_elliptic_curve_point_format); #endif } } } else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS printf("Server TLS Invalid len %u vs %u\n", s_offset+extension_len, total_len); #endif @@ -1837,33 +1989,33 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(flow->protos.tls_quic.ssl_version == 0) flow->protos.tls_quic.ssl_version = tls_version; - ja3_str_len = ndpi_snprintf(ja3_str, JA_STR_LEN, "%u,", ja3.server.tls_handshake_version); + ja_str_len = ndpi_snprintf(ja_str, JA_STR_LEN, "%u,", ja.server.tls_handshake_version); - for(i=0; (i<ja3.server.num_cipher) && (JA_STR_LEN > ja3_str_len); i++) { - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.server.cipher[i]); + for(i=0; (i<ja.server.num_ciphers) && (JA_STR_LEN > ja_str_len); i++) { + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%s%u", (i > 0) ? "-" : "", ja.server.cipher[i]); - if(rc <= 0) break; else ja3_str_len += rc; + if(rc <= 0) break; else ja_str_len += rc; } - if(JA_STR_LEN > ja3_str_len) { - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, ","); - if(rc > 0 && ja3_str_len + rc < JA_STR_LEN) ja3_str_len += rc; + if(JA_STR_LEN > ja_str_len) { + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, ","); + if(rc > 0 && ja_str_len + rc < JA_STR_LEN) ja_str_len += rc; } /* ********** */ - for(i=0; (i<ja3.server.num_tls_extension) && (JA_STR_LEN > ja3_str_len); i++) { - int rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.server.tls_extension[i]); + for(i=0; (i<ja.server.num_tls_extensions) && (JA_STR_LEN > ja_str_len); i++) { + int rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%s%u", (i > 0) ? "-" : "", ja.server.tls_extension[i]); - if(rc <= 0) break; else ja3_str_len += rc; + if(rc <= 0) break; else ja_str_len += rc; } #ifdef DEBUG_TLS - printf("[JA3] Server: %s \n", ja3_str); + printf("[JA3] Server: %s \n", ja_str); #endif ndpi_MD5Init(&ctx); - ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); + ndpi_MD5Update(&ctx, (const unsigned char *)ja_str, strlen(ja_str)); ndpi_MD5Final(md5_hash, &ctx); for(i=0, j=0; i<16; i++) { @@ -1879,25 +2031,26 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, u_int16_t cipher_len, cipher_offset; u_int8_t cookie_len = 0; - ja3.client.num_cipher = 0; - ja3.client.num_tls_extension = 0; - ja3.client.num_elliptic_curve = 0; - ja3.client.num_elliptic_curve_point_format = 0; - ja3.client.signature_algorithms[0] = '\0'; - ja3.client.supported_versions[0] = '\0'; - ja3.client.alpn[0] = '\0'; + ja.client.num_ciphers = 0; + ja.client.num_tls_extensions = 0; + ja.client.num_elliptic_curve = 0; + ja.client.num_elliptic_curve_point_format = 0; + ja.client.num_signature_algorithms = 0; + ja.client.num_supported_versions = 0; + ja.client.signature_algorithms_str[0] = '\0'; + ja.client.alpn[0] = '\0'; - flow->protos.tls_quic.ssl_version = ja3.client.tls_handshake_version = tls_version; + flow->protos.tls_quic.ssl_version = ja.client.tls_handshake_version = tls_version; if(flow->protos.tls_quic.ssl_version < 0x0303) /* < TLSv1.2 */ { char str[32], buf[32]; u_int8_t unknown_tls_version; - + snprintf(str, sizeof(str), "%s", ndpi_ssl_version2str(buf, sizeof(buf), flow->protos.tls_quic.ssl_version, &unknown_tls_version)); ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_OBSOLETE_VERSION, str); } - + if((session_id_len+base_offset+3) > packet->payload_packet_len) return(0); /* Not found */ @@ -1938,12 +2091,12 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, printf("Client TLS [non-GREASE cipher suite: %u/0x%04X] [%d/%u]\n", cipher_id, cipher_id, i, cipher_len); #endif - if(ja3.client.num_cipher < MAX_NUM_JA) - ja3.client.cipher[ja3.client.num_cipher++] = cipher_id; + if(ja.client.num_ciphers < MAX_NUM_JA) + ja.client.cipher[ja.client.num_ciphers++] = cipher_id; else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS - printf("Client TLS Invalid cipher %u\n", ja3.client.num_cipher); + printf("Client TLS Invalid cipher %u\n", ja.client.num_ciphers); #endif } @@ -2013,7 +2166,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, this_is_not_safari); #endif } else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS printf("Client TLS Invalid len %u vs %u\n", (cipher_offset+cipher_len), total_len); #endif @@ -2078,12 +2231,12 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, ((packet->payload[extn_off] & 0xF) != 0xA)) { /* Skip GREASE */ - if(ja3.client.num_tls_extension < MAX_NUM_JA) - ja3.client.tls_extension[ja3.client.num_tls_extension++] = extension_id; + if(ja.client.num_tls_extensions < MAX_NUM_JA) + ja.client.tls_extension[ja.client.num_tls_extensions++] = extension_id; else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS - printf("Client TLS Invalid extensions %u\n", ja3.client.num_tls_extension); + printf("Client TLS Invalid extensions %u\n", ja.client.num_tls_extensions); #endif } } @@ -2105,11 +2258,11 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif if(ndpi_is_valid_hostname(sni, sni_len) == 0) { ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, sni); - + /* This looks like an attack */ ndpi_set_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT, NULL); } - + if(!is_quic) { if(ndpi_match_hostname_protocol(ndpi_struct, flow, __get_master(ndpi_struct, flow), sni, sni_len)) flow->protos.tls_quic.subprotocol_detected = 1; @@ -2118,13 +2271,12 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, flow->protos.tls_quic.subprotocol_detected = 1; } - if(flow->protos.tls_quic.subprotocol_detected == 0 && - check_sni_is_numeric_ip(sni) == 1) { + if((flow->protos.tls_quic.subprotocol_detected == 0) + && (check_sni_is_numeric_ip(sni) == 1)) { ndpi_set_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST, sni); } - if(ndpi_check_dga_name(ndpi_struct, flow, - sni, 1, 0)) { + if(ndpi_check_dga_name(ndpi_struct, flow, sni, 1, 0)) { #ifdef DEBUG_TLS printf("[TLS] SNI: (DGA) [%s]\n", sni); #endif @@ -2164,18 +2316,18 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if((s_group == 0) || (packet->payload[s_offset+i] != packet->payload[s_offset+i+1]) || ((packet->payload[s_offset+i] & 0xF) != 0xA)) { /* Skip GREASE */ - if(ja3.client.num_elliptic_curve < MAX_NUM_JA) - ja3.client.elliptic_curve[ja3.client.num_elliptic_curve++] = s_group; + if(ja.client.num_elliptic_curve < MAX_NUM_JA) + ja.client.elliptic_curve[ja.client.num_elliptic_curve++] = s_group; else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS - printf("Client TLS Invalid num elliptic %u\n", ja3.client.num_elliptic_curve); + printf("Client TLS Invalid num elliptic %u\n", ja.client.num_elliptic_curve); #endif } } } } else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS printf("Client TLS Invalid len %u vs %u\n", (s_offset+extension_len-1), total_len); #endif @@ -2194,25 +2346,25 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, printf("Client TLS [EllipticCurveFormat: %u]\n", s_group); #endif - if(ja3.client.num_elliptic_curve_point_format < MAX_NUM_JA) - ja3.client.elliptic_curve_point_format[ja3.client.num_elliptic_curve_point_format++] = s_group; + if(ja.client.num_elliptic_curve_point_format < MAX_NUM_JA) + ja.client.elliptic_curve_point_format[ja.client.num_elliptic_curve_point_format++] = s_group; else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS - printf("Client TLS Invalid num elliptic %u\n", ja3.client.num_elliptic_curve_point_format); + printf("Client TLS Invalid num elliptic %u\n", ja.client.num_elliptic_curve_point_format); #endif } } } else { - invalid_ja3 = 1; + invalid_ja = 1; #ifdef DEBUG_TLS printf("Client TLS Invalid len %u vs %u\n", s_offset+extension_len, total_len); #endif } } else if(extension_id == 13 /* signature algorithms */ && offset+extension_offset+1 < total_len) { - int s_offset = offset+extension_offset, safari_signature_algorithms = 0, chrome_signature_algorithms = 0, - duplicate_found = 0, last_signature = 0; + int s_offset = offset+extension_offset, safari_signature_algorithms = 0, + chrome_signature_algorithms = 0, duplicate_found = 0, last_signature = 0, id; u_int16_t tot_signature_algorithms_len = ntohs(*((u_int16_t*)&packet->payload[s_offset])); #ifdef DEBUG_TLS @@ -2220,20 +2372,25 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif s_offset += 2; - tot_signature_algorithms_len = ndpi_min((sizeof(ja3.client.signature_algorithms) / 2) - 1, tot_signature_algorithms_len); + tot_signature_algorithms_len = ndpi_min((sizeof(ja.client.signature_algorithms_str) / 2) - 1, tot_signature_algorithms_len); -#ifdef TLS_HANDLE_SIGNATURE_ALGORITMS - size_t size = ndpi_min(tot_signature_algorithms_len / 2, MAX_NUM_TLS_SIGNATURE_ALGORITHMS); + size_t sa_size = ndpi_min(tot_signature_algorithms_len / 2, MAX_NUM_TLS_SIGNATURE_ALGORITHMS); - if (s_offset + 2 * size <= packet->payload_packet_len) { - flow->protos.tls_quic.num_tls_signature_algorithms = size; - memcpy(flow->protos.tls_quic.client_signature_algorithms, - &packet->payload[s_offset], 2 /* 16 bit */ * size); +#ifdef TLS_HANDLE_SIGNATURE_ALGORITMS + if (s_offset + 2 * sa_size <= packet->payload_packet_len) { + flow->protos.tls_quic.num_tls_signature_algorithms = sa_size; + memcpy(flow->protos.tls_quic.client_signature_algorithms, + &packet->payload[s_offset], 2 /* 16 bit */ * sa_size); } #endif - for(i=0; i<tot_signature_algorithms_len && s_offset+i<total_len; i++) { - int rc = ndpi_snprintf(&ja3.client.signature_algorithms[i*2], sizeof(ja3.client.signature_algorithms)-i*2, "%02X", packet->payload[s_offset+i]); + ja.client.num_signature_algorithms = ndpi_min(sa_size, MAX_NUM_JA); + + for(i=0, id=0; i<tot_signature_algorithms_len && s_offset+i<total_len; i++) { + int rc = ndpi_snprintf(&ja.client.signature_algorithms_str[i*2], + sizeof(ja.client.signature_algorithms_str)-i*2, + "%02X", packet->payload[s_offset+i]); + ja.client.signature_algorithms[id++] = ntohs(*(u_int16_t*)&packet->payload[s_offset+i*2]); if(rc < 0) break; } @@ -2329,13 +2486,13 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif if(i > 0 && i >= tot_signature_algorithms_len) { - ja3.client.signature_algorithms[i*2 - 1] = '\0'; + ja.client.signature_algorithms_str[i*2 - 1] = '\0'; } else { - ja3.client.signature_algorithms[i*2] = '\0'; + ja.client.signature_algorithms_str[i*2] = '\0'; } #ifdef DEBUG_TLS - printf("Client TLS [SIGNATURE_ALGORITHMS: %s]\n", ja3.client.signature_algorithms); + printf("Client TLS [SIGNATURE_ALGORITHMS: %s]\n", ja.client.signature_algorithms_str); #endif } else if(extension_id == 16 /* application_layer_protocol_negotiation */ && offset+extension_offset+1 < total_len) { @@ -2393,14 +2550,14 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, } } - alpn_str_len = ndpi_min(sizeof(ja3.client.alpn), (size_t)alpn_str_len); - memcpy(ja3.client.alpn, alpn_str, alpn_str_len); + alpn_str_len = ndpi_min(sizeof(ja.client.alpn), (size_t)alpn_str_len); + memcpy(ja.client.alpn, alpn_str, alpn_str_len); if(alpn_str_len > 0) - ja3.client.alpn[alpn_str_len - 1] = '\0'; + ja.client.alpn[alpn_str_len - 1] = '\0'; /* Replace , with - as in JA3 */ - for(i=0; ja3.client.alpn[i] != '\0'; i++) - if(ja3.client.alpn[i] == ',') ja3.client.alpn[i] = '-'; + for(i=0; ja.client.alpn[i] != '\0'; i++) + if(ja.client.alpn[i] == ',') ja.client.alpn[i] = '-'; } else if(extension_id == 43 /* supported versions */ && offset+extension_offset < total_len) { @@ -2416,7 +2573,6 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(version_len == (extension_len-1)) { u_int8_t j; - u_int16_t supported_versions_offset = 0; s_offset++; @@ -2440,17 +2596,13 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, else version_str_len += rc; - rc = ndpi_snprintf(&ja3.client.supported_versions[supported_versions_offset], - sizeof(ja3.client.supported_versions)-supported_versions_offset, - "%s%04X", (j > 0) ? "-" : "", tls_version); - - if(rc > 0) - supported_versions_offset += rc; + if(ja.client.num_supported_versions < MAX_NUM_JA) + ja.client.supported_versions[ja.client.num_supported_versions++] = tls_version; } } #ifdef DEBUG_TLS - printf("Client TLS [SUPPORTED_VERSIONS: %s]\n", ja3.client.supported_versions); + printf("Client TLS [SUPPORTED_VERSIONS: %s]\n", ja.client.supported_versions_str); #endif if(flow->protos.tls_quic.tls_supported_versions == NULL) @@ -2583,51 +2735,52 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #endif } /* while */ - if(!invalid_ja3) { + if(!invalid_ja) { + /* Compute JA3 client */ int rc; compute_ja3c: - ja3_str_len = ndpi_snprintf(ja3_str, JA_STR_LEN, "%u,", ja3.client.tls_handshake_version); + ja_str_len = ndpi_snprintf(ja_str, JA_STR_LEN, "%u,", ja.client.tls_handshake_version); - for(i=0; i<ja3.client.num_cipher; i++) { - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.client.cipher[i]); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; else break; + for(i=0; i<ja.client.num_ciphers; i++) { + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%s%u", + (i > 0) ? "-" : "", ja.client.cipher[i]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; else break; } - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, ","); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, ","); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; /* ********** */ - for(i=0; i<ja3.client.num_tls_extension; i++) { - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.client.tls_extension[i]); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; else break; + for(i=0; i<ja.client.num_tls_extensions; i++) { + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%s%u", + (i > 0) ? "-" : "", ja.client.tls_extension[i]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; else break; } - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, ","); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, ","); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; /* ********** */ - for(i=0; i<ja3.client.num_elliptic_curve; i++) { - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.client.elliptic_curve[i]); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; else break; + for(i=0; i<ja.client.num_elliptic_curve; i++) { + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%s%u", + (i > 0) ? "-" : "", ja.client.elliptic_curve[i]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; else break; } - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, ","); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, ","); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; - for(i=0; i<ja3.client.num_elliptic_curve_point_format; i++) { - rc = ndpi_snprintf(&ja3_str[ja3_str_len], JA_STR_LEN-ja3_str_len, "%s%u", - (i > 0) ? "-" : "", ja3.client.elliptic_curve_point_format[i]); - if((rc > 0) && (ja3_str_len + rc < JA_STR_LEN)) ja3_str_len += rc; else break; + for(i=0; i<ja.client.num_elliptic_curve_point_format; i++) { + rc = ndpi_snprintf(&ja_str[ja_str_len], JA_STR_LEN-ja_str_len, "%s%u", + (i > 0) ? "-" : "", ja.client.elliptic_curve_point_format[i]); + if((rc > 0) && (ja_str_len + rc < JA_STR_LEN)) ja_str_len += rc; else break; } ndpi_MD5Init(&ctx); - ndpi_MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str)); + ndpi_MD5Update(&ctx, (const unsigned char *)ja_str, strlen(ja_str)); ndpi_MD5Final(md5_hash, &ctx); for(i=0, j=0; i<16; i++) { @@ -2650,6 +2803,9 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, if(rc1 == 0) ndpi_set_risk(ndpi_struct, flow, NDPI_MALICIOUS_JA3, flow->protos.tls_quic.ja3_client); } + + ndpi_compute_ja4(ndpi_struct, flow, quic_version, &ja); + /* End JA3/JA4 */ } /* Before returning to the caller we need to make a final check */ @@ -2672,14 +2828,14 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, ) { /* This is a bit suspicious */ ndpi_set_risk(ndpi_struct, flow, NDPI_TLS_MISSING_SNI, NULL); - + if(flow->protos.tls_quic.advertised_alpns != NULL) { char buf[256], *tmp, *item; snprintf(buf, sizeof(buf), "%s", flow->protos.tls_quic.advertised_alpns); item = strtok_r(buf, ",", &tmp); - + while(item != NULL) { if(item[0] == 'h') { /* Example 'h2' */ @@ -2690,7 +2846,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, } } } - + return(2 /* Client Certificate */); } else { #ifdef DEBUG_TLS |