aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/ndpiReader.c31
-rw-r--r--example/reader_util.c4
-rw-r--r--src/include/ndpi_typedefs.h3
-rw-r--r--src/lib/ndpi_main.c3
-rw-r--r--src/lib/protocols/tls.c79
5 files changed, 95 insertions, 25 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c
index 0baa51000..aec78d778 100644
--- a/example/ndpiReader.c
+++ b/example/ndpiReader.c
@@ -80,7 +80,7 @@ u_int8_t enable_protocol_guess = 1, enable_payload_analyzer = 0, num_bin_cluster
u_int8_t verbose = 0, enable_joy_stats = 0;
int nDPI_LogLevel = 0;
char *_debug_protocols = NULL;
-u_int8_t human_readeable_string_len = 5;
+u_int8_t human_readeable_string_len = 5, enable_ja3_plus = 0;
u_int8_t max_num_udp_dissected_pkts = 24 /* 8 is enough for most protocols, Signal and SnapchatCall require more */, max_num_tcp_dissected_pkts = 80 /* due to telnet */;
static u_int32_t pcap_analysis_duration = (u_int32_t)-1;
static u_int16_t decode_tunnels = 0;
@@ -274,7 +274,7 @@ void ndpiCheckHostStringMatch(char *testChar) {
if(!testChar)
return;
- ndpi_str = ndpi_init_detection_module(ndpi_no_prefs);
+ ndpi_str = ndpi_init_detection_module(enable_ja3_plus ? ndpi_enable_ja3_plus : ndpi_no_prefs);
ndpi_finalize_initialization(ndpi_str);
// Display ALL Host strings ie host_match[] ?
@@ -440,7 +440,7 @@ static void help(u_int long_help) {
"[-f <filter>][-s <duration>][-m <duration>][-b <num bin clusters>]\n"
" [-p <protos>][-l <loops> [-q][-d][-J][-h][-D][-e <len>][-t][-v <level>]\n"
" [-n <threads>][-w <file>][-c <file>][-C <file>][-j <file>][-x <file>]\n"
- " [-r <file>][-j <file>][-S <file>][-T <num>][-U <num>] [-x <domain>]\n\n"
+ " [-r <file>][-j <file>][-S <file>][-T <num>][-U <num>] [-x <domain>][-z]\n\n"
"Usage:\n"
" -i <file.pcap|device> | Specify a pcap file/playlist to read packets from or a\n"
" | device for live capture (comma-separated list)\n"
@@ -491,6 +491,7 @@ static void help(u_int long_help) {
" -D | Enable DoH traffic analysis based on content (no DPI)\n"
" -x <domain> | Check domain name [Test only]\n"
" -I | Ignore VLAN id for flow hash calculation\n"
+ " -z | Enable JA3+\n"
,
human_readeable_string_len,
min_pattern_len, max_pattern_len, max_num_packets_per_flow, max_packet_payload_dissection,
@@ -767,7 +768,7 @@ static void parseOptions(int argc, char **argv) {
}
#endif
- while((opt = getopt_long(argc, argv, "b:e:c:C:dDf:g:i:Ij:S:hp:P:l:r:s:tu:v:V:n:Jrp:x:w:q0123:456:7:89:m:T:U:",
+ while((opt = getopt_long(argc, argv, "b:e:c:C:dDf:g:i:Ij:S:hp:pP:l:r:s:tu:v:V:n:Jrp:x:w:zq0123:456:7:89:m:T:U:",
longopts, &option_idx)) != EOF) {
#ifdef DEBUG_TRACE
if(trace) fprintf(trace, " #### -%c [%s] #### \n", opt, optarg ? optarg : "");
@@ -966,6 +967,10 @@ static void parseOptions(int argc, char **argv) {
if(max_num_udp_dissected_pkts < 3) max_num_udp_dissected_pkts = 3;
break;
+ case 'z':
+ enable_ja3_plus = 1;
+ break;
+
default:
help(0);
break;
@@ -3516,7 +3521,7 @@ static void dgaUnitTest() {
};
int i;
NDPI_PROTOCOL_BITMASK all;
- struct ndpi_detection_module_struct *ndpi_str = ndpi_init_detection_module(ndpi_no_prefs);
+ struct ndpi_detection_module_struct *ndpi_str = ndpi_init_detection_module(enable_ja3_plus ? ndpi_enable_ja3_plus : ndpi_no_prefs);
assert(ndpi_str != NULL);
@@ -3922,7 +3927,7 @@ void jitterUnitTest() {
@brief MAIN FUNCTION
**/
#ifdef APP_HAS_OWN_MAIN
-int orginal_main(int argc, char **argv) {
+int original_main(int argc, char **argv) {
#else
int main(int argc, char **argv) {
#endif
@@ -3933,11 +3938,6 @@ int orginal_main(int argc, char **argv) {
return(-1);
}
- gettimeofday(&startup_time, NULL);
- ndpi_info_mod = ndpi_init_detection_module(ndpi_no_prefs);
-
- if(ndpi_info_mod == NULL) return -1;
-
// hwUnitTest2();
/* Internal checks */
@@ -3954,17 +3954,22 @@ int orginal_main(int argc, char **argv) {
ndpi_self_check_host_match();
analysisUnitTest();
rulesUnitTest();
+
+
+ gettimeofday(&startup_time, NULL);
memset(ndpi_thread_info, 0, sizeof(ndpi_thread_info));
parseOptions(argc, argv);
+ ndpi_info_mod = ndpi_init_detection_module(enable_ja3_plus ? ndpi_enable_ja3_plus : ndpi_no_prefs);
+
+ if(ndpi_info_mod == NULL) return -1;
+
if(domain_to_check) {
ndpiCheckHostStringMatch(domain_to_check);
exit(0);
}
- if(enable_doh_dot_detection) init_doh_bins();
-
if(!quiet_mode) {
printf("\n-----------------------------------------------------------\n"
"* NOTE: This is demo app to show *some* nDPI features.\n"
diff --git a/example/reader_util.c b/example/reader_util.c
index 257b5658f..95c6b04ad 100644
--- a/example/reader_util.c
+++ b/example/reader_util.c
@@ -402,8 +402,10 @@ static int parse_debug_proto(struct ndpi_detection_module_struct *ndpi_mod, char
/* ***************************************************** */
extern char *_debug_protocols;
+extern u_int8_t enable_ja3_plus;
static int _debug_protocols_ok = 0;
+
struct ndpi_workflow* ndpi_workflow_init(const struct ndpi_workflow_prefs * prefs,
pcap_t * pcap_handle) {
struct ndpi_detection_module_struct * module;
@@ -413,7 +415,7 @@ struct ndpi_workflow* ndpi_workflow_init(const struct ndpi_workflow_prefs * pref
set_ndpi_flow_malloc(NULL), set_ndpi_flow_free(NULL);
/* TODO: just needed here to init ndpi ndpi_malloc wrapper */
- module = ndpi_init_detection_module(ndpi_no_prefs);
+ module = ndpi_init_detection_module(enable_ja3_plus ? ndpi_enable_ja3_plus : ndpi_no_prefs);
if(module == NULL) {
LOG(NDPI_LOG_ERROR, "global structure initialization failed\n");
diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h
index cd167a24e..e2e27737c 100644
--- a/src/include/ndpi_typedefs.h
+++ b/src/include/ndpi_typedefs.h
@@ -1050,7 +1050,7 @@ struct ndpi_detection_module_struct {
u_int32_t current_ts;
u_int32_t ticks_per_second;
u_int16_t num_tls_blocks_to_follow;
- u_int8_t skip_tls_blocks_until_change_cipher:1, _notused:7;
+ u_int8_t skip_tls_blocks_until_change_cipher:1, enable_ja3_plus:1, _notused:6;
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
void *user_data;
@@ -1453,6 +1453,7 @@ typedef enum
ndpi_no_prefs = 0,
ndpi_dont_load_tor_hosts,
ndpi_dont_init_libgcrypt,
+ ndpi_enable_ja3_plus
} ndpi_prefs;
typedef struct {
diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c
index 36ec8db5b..840f64513 100644
--- a/src/lib/ndpi_main.c
+++ b/src/lib/ndpi_main.c
@@ -2156,6 +2156,9 @@ struct ndpi_detection_module_struct *ndpi_init_detection_module(ndpi_init_prefs
NDPI_BITMASK_RESET(ndpi_str->debug_bitmask);
#endif /* NDPI_ENABLE_DEBUG_MESSAGES */
+ if(prefs & ndpi_enable_ja3_plus)
+ ndpi_str->enable_ja3_plus = 1;
+
#ifdef HAVE_LIBGCRYPT
if(!(prefs & ndpi_dont_init_libgcrypt)) {
if(!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c
index 8597f05bb..99bbd5deb 100644
--- a/src/lib/protocols/tls.c
+++ b/src/lib/protocols/tls.c
@@ -1006,8 +1006,9 @@ static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndp
/* https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 */
-#define JA3_STR_LEN 1024
-#define MAX_NUM_JA3 512
+#define JA3_STR_LEN 1024
+#define MAX_NUM_JA3 512
+#define MAX_JA3_STRLEN 128
struct ja3_info {
u_int16_t tls_handshake_version;
@@ -1015,6 +1016,7 @@ struct ja3_info {
u_int16_t num_tls_extension, tls_extension[MAX_NUM_JA3];
u_int16_t num_elliptic_curve, elliptic_curve[MAX_NUM_JA3];
u_int16_t num_elliptic_curve_point_format, elliptic_curve_point_format[MAX_NUM_JA3];
+ char signature_algorithms[MAX_JA3_STRLEN], supported_versions[MAX_JA3_STRLEN], alpn[MAX_JA3_STRLEN];
};
/* **************************************** */
@@ -1324,7 +1326,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
if(ndpi_match_hostname_protocol(ndpi_struct, flow, NDPI_PROTOCOL_QUIC, buffer, strlen(buffer)))
flow->l4.tcp.tls.subprotocol_detected = 1;
}
-
+
if(ndpi_check_dga_name(ndpi_struct, flow,
flow->protos.tls_quic_stun.tls_quic.client_requested_server_name, 1)) {
char *sni = flow->protos.tls_quic_stun.tls_quic.client_requested_server_name;
@@ -1333,7 +1335,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
#ifdef DEBUG_TLS
printf("[TLS] SNI: (DGA) [%s]\n", flow->protos.tls_quic_stun.tls_quic.client_requested_server_name);
#endif
-
+
if((len >= 4)
&& strcmp(&sni[len-4], ".com") /* Check if it ends in .com or .net */
&& strcmp(&sni[len-4], ".net")
@@ -1394,7 +1396,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
printf("Client SSL [EllipticCurveFormat: len=%u]\n", extension_len);
#endif
if((s_offset+extension_len-1) <= total_len) {
- for(i=0; i<extension_len-1;i++) {
+ for(i=0; i<extension_len-1; i++) {
u_int8_t s_group = packet->payload[s_offset+i];
#ifdef DEBUG_TLS
@@ -1416,11 +1418,55 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
printf("Client SSL Invalid len %u vs %u\n", s_offset+extension_len, total_len);
#endif
}
+ } else if(extension_id == 13 /* signature algorithms */) {
+ u_int16_t s_offset = offset+extension_offset;
+ u_int16_t tot_signature_algorithms_len = ntohs(*((u_int16_t*)&packet->payload[s_offset]));
+
+#ifdef DEBUG_TLS
+ printf("Client SSL [SIGNATURE_ALGORITHMS: block_len=%u/len=%u]\n", extension_len, tot_signature_algorithms_len);
+#endif
+
+ s_offset += 2;
+ tot_signature_algorithms_len = ndpi_min((sizeof(ja3.signature_algorithms) / 2) - 1, tot_signature_algorithms_len);
+
+ for(i=0; i<tot_signature_algorithms_len; i++) {
+ int rc = snprintf(&ja3.signature_algorithms[i*2], sizeof(ja3.signature_algorithms)-i*2, "%02X", packet->payload[s_offset+i]);
+
+ if(rc < 0) break;
+ }
+
+ ja3.signature_algorithms[i*2] = '\0';
+
+#ifdef DEBUG_TLS
+ printf("Client SSL [SIGNATURE_ALGORITHMS: %s]\n", ja3.signature_algorithms);
+#endif
+ } else if(extension_id == 43 /* supported versions */) {
+ u_int16_t s_offset = offset+extension_offset;
+ u_int8_t tot_supported_versions_len = (u_int8_t)packet->payload[s_offset];
+
+#ifdef DEBUG_TLS
+ printf("Client SSL [SUPPORTED_VERSIONS: block_len=%u/len=%u]\n", extension_len, tot_supported_versions_len);
+#endif
+
+ s_offset += 1;
+ tot_supported_versions_len = ndpi_min((sizeof(ja3.supported_versions) / 2) - 1, tot_supported_versions_len);
+
+ for(i=0; i<tot_supported_versions_len; i++) {
+ int rc = snprintf(&ja3.supported_versions[i*2], sizeof(ja3.supported_versions)-i*2, "%02X", packet->payload[s_offset+i]);
+
+ if(rc < 0) break;
+ }
+
+ ja3.supported_versions[i*2] = '\0';
+
+#ifdef DEBUG_TLS
+ printf("Client SSL [SUPPORTED_VERSIONS: %s]\n", ja3.supported_versions);
+#endif
} else if(extension_id == 16 /* application_layer_protocol_negotiation */) {
u_int16_t s_offset = offset+extension_offset;
u_int16_t tot_alpn_len = ntohs(*((u_int16_t*)&packet->payload[s_offset]));
char alpn_str[256];
- u_int8_t alpn_str_len = 0;
+ u_int8_t alpn_str_len = 0, i;
#ifdef DEBUG_TLS
printf("Client SSL [ALPN: block_len=%u/len=%u]\n", extension_len, tot_alpn_len);
@@ -1459,6 +1505,13 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
#endif
if(flow->protos.tls_quic_stun.tls_quic.alpn == NULL)
flow->protos.tls_quic_stun.tls_quic.alpn = ndpi_strdup(alpn_str);
+
+ snprintf(ja3.alpn, sizeof(ja3.alpn), "%s", alpn_str);
+
+ /* Replace , with - as in JA3 */
+ for(i=0; ja3.alpn[i] != '\0'; i++)
+ if(ja3.alpn[i] == ',') ja3.alpn[i] = '-';
+
} else if(extension_id == 43 /* supported versions */) {
u_int16_t s_offset = offset+extension_offset;
u_int8_t version_len = packet->payload[s_offset];
@@ -1656,6 +1709,12 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
if(rc > 0 && ja3_str_len + rc < JA3_STR_LEN) ja3_str_len += rc; else break;
}
+ if(ndpi_struct->enable_ja3_plus) {
+ rc = snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len,
+ ",%s,%s,%s", ja3.signature_algorithms, ja3.supported_versions, ja3.alpn);
+ if(rc > 0 && ja3_str_len + rc < JA3_STR_LEN) ja3_str_len += rc;
+ }
+
#ifdef DEBUG_TLS
printf("[JA3] Client: %s \n", ja3_str);
#endif
@@ -1670,18 +1729,18 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
md5_hash[i]);
if(rc > 0) j += rc; else break;
}
-
+
#ifdef DEBUG_TLS
printf("[JA3] Client: %s \n", flow->protos.tls_quic_stun.tls_quic.ja3_client);
#endif
-
+
if(ndpi_struct->malicious_ja3_automa.ac_automa != NULL) {
u_int16_t rc1 = ndpi_match_string(ndpi_struct->malicious_ja3_automa.ac_automa,
flow->protos.tls_quic_stun.tls_quic.ja3_client);
-
+
if(rc1 > 0)
ndpi_set_risk(flow, NDPI_MALICIOUS_JA3);
- }
+ }
}
/* Before returning to the caller we need to make a final check */