diff options
-rw-r--r-- | src/include/ndpi_main.h | 2 | ||||
-rw-r--r-- | src/include/ndpi_typedefs.h | 1 | ||||
-rw-r--r-- | src/include/ndpi_utils.h | 13 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 1 | ||||
-rw-r--r-- | src/lib/ndpi_utils.c | 48 | ||||
-rw-r--r-- | src/lib/protocols/tls.c | 99 | ||||
-rw-r--r-- | tests/result/tls_invalid_reads.pcap.out | 2 |
7 files changed, 147 insertions, 19 deletions
diff --git a/src/include/ndpi_main.h b/src/include/ndpi_main.h index 78e8fdb4a..c8d9b06ae 100644 --- a/src/include/ndpi_main.h +++ b/src/include/ndpi_main.h @@ -152,6 +152,8 @@ extern "C" { const u_int8_t ** l4ptr, u_int16_t * l4len, u_int8_t * nxt_hdr); void ndpi_set_risk(struct ndpi_flow_struct *flow, ndpi_risk_enum r); + int ndpi_is_printable_string(char const * const str, size_t len); + float ndpi_calculate_entropy(u_int8_t const * const buf, size_t len); #ifdef __cplusplus } #endif diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 860e2da1c..aeac33b30 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -102,6 +102,7 @@ typedef enum { NDPI_DESKTOP_OR_FILE_SHARING_SESSION, /* 30 */ NDPI_TLS_UNCOMMON_ALPN, NDPI_TLS_CERT_VALIDITY_TOO_LONG, + NDPI_TLS_EXTENSION_SUSPICIOUS, /* Leave this as last member */ NDPI_MAX_RISK /* must be <= 63 due to (**) */ diff --git a/src/include/ndpi_utils.h b/src/include/ndpi_utils.h index b8176cc02..983aae283 100644 --- a/src/include/ndpi_utils.h +++ b/src/include/ndpi_utils.h @@ -16,4 +16,17 @@ extern void printRawData(const uint8_t *ptr, size_t len); //extern uint8_t add_segment_to_buffer( struct ndpi_flow_struct *flow, struct ndpi_tcphdr const * tcph, uint32_t waited); //extern uint8_t check_for_sequence( struct ndpi_flow_struct *flow, struct ndpi_tcphdr const * tcph); +/* **************************************** */ + +/* Can't call libc functions from kernel space, define some stub instead */ + +#define ndpi_isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) +#define ndpi_isdigit(ch) ((ch) >= '0' && (ch) <= '9') +#define ndpi_isspace(ch) (((ch) >= '\t' && (ch) <= '\r') || ((ch) == ' ')) +#define ndpi_isprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e) +#define ndpi_ispunct(ch) (((ch) >= '!' && (ch) <= '/') || \ + ((ch) >= ':' && (ch) <= '@') || \ + ((ch) >= '[' && (ch) <= '`') || \ + ((ch) >= '{' && (ch) <= '~')) + #endif diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index 09af06680..13148f44e 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -103,6 +103,7 @@ static ndpi_risk_info ndpi_known_risks[] = { { NDPI_DESKTOP_OR_FILE_SHARING_SESSION, NDPI_RISK_LOW, CLIENT_FAIR_RISK_PERCENTAGE }, { NDPI_TLS_UNCOMMON_ALPN, NDPI_RISK_MEDIUM, CLIENT_HIGH_RISK_PERCENTAGE }, { NDPI_TLS_CERT_VALIDITY_TOO_LONG, NDPI_RISK_MEDIUM, CLIENT_FAIR_RISK_PERCENTAGE }, + { NDPI_TLS_EXTENSION_SUSPICIOUS, NDPI_RISK_HIGH, CLIENT_HIGH_RISK_PERCENTAGE }, /* Leave this as last member */ { NDPI_MAX_RISK, NDPI_RISK_LOW, CLIENT_FAIR_RISK_PERCENTAGE } diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index 3a3c18aff..7c27f8aed 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include <errno.h> +#include <math.h> #include <sys/types.h> @@ -1735,6 +1736,8 @@ const char* ndpi_risk2str(ndpi_risk_enum risk) { case NDPI_TLS_CERT_VALIDITY_TOO_LONG: return("TLS certificate validity longer than 13 months"); + case NDPI_TLS_EXTENSION_SUSPICIOUS: + return("TLS extension suspicious"); default: snprintf(buf, sizeof(buf), "%d", (int)risk); @@ -2001,4 +2004,49 @@ void ndpi_set_risk(struct ndpi_flow_struct *flow, ndpi_risk_enum r) { flow->risk |= v; } +/* ******************************************************************** */ + +int ndpi_is_printable_string(char const * const str, size_t len) +{ + for (size_t i = 0; i < len; ++i) + { + if (ndpi_isprint(str[i]) == 0) + { + return 0; + } + } + + return 1; +} + +/* ******************************************************************** */ + +float ndpi_calculate_entropy(u_int8_t const * const buf, size_t len) +{ + float entropy = 0.0f; + u_int32_t byte_counters[256]; + memset(byte_counters, 0, sizeof(byte_counters)); + + for (size_t i = 0; i < len; ++i) + { + if (buf[i] == i) + { + byte_counters[i]++; + } + } + + for (size_t i = 0; i < sizeof(byte_counters) / sizeof(byte_counters[0]); ++i) + { + if (byte_counters[i] == 0) + { + continue; + } + + float p = 1.0f * byte_counters[i] / len; + entropy -= p * log2f(p); + } + + entropy *= -1.0f; + return entropy; +} diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index b2d3dc754..ae77ea052 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -222,19 +222,6 @@ void ndpi_search_tls_tcp_memory(struct ndpi_detection_module_struct *ndpi_struct /* **************************************** */ -/* Can't call libc functions from kernel space, define some stub instead */ - -#define ndpi_isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) -#define ndpi_isdigit(ch) ((ch) >= '0' && (ch) <= '9') -#define ndpi_isspace(ch) (((ch) >= '\t' && (ch) <= '\r') || ((ch) == ' ')) -#define ndpi_isprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e) -#define ndpi_ispunct(ch) (((ch) >= '!' && (ch) <= '/') || \ - ((ch) >= ':' && (ch) <= '@') || \ - ((ch) >= '[' && (ch) <= '`') || \ - ((ch) >= '{' && (ch) <= '~')) - -/* **************************************** */ - static void cleanupServerName(char *buffer, int buffer_len) { u_int i; @@ -527,6 +514,11 @@ static void processCertificateElements(struct ndpi_detection_module_struct *ndpi flow->protos.tls_quic_stun.tls_quic.client_requested_server_name, len, packet->payload_packet_len-i-len); #endif + if (ndpi_is_printable_string(dNSName, len) == 0) + { + ndpi_set_risk(flow, NDPI_TLS_EXTENSION_SUSPICIOUS); + } + if(matched_name == 0) { if(flow->protos.tls_quic_stun.tls_quic.client_requested_server_name[0] == '\0') matched_name = 1; /* No SNI */ @@ -1095,11 +1087,6 @@ static void tlsCheckUncommonALPN(struct ndpi_flow_struct *flow) "doq-i00" }; - /* - * If the ALPN list increases in size, iterating over all items for every incoming ALPN may - * have a performance impact. A hash map could solve this issue. - */ - char * alpn_start = flow->protos.tls_quic_stun.tls_quic.alpn; char * comma_or_nul = alpn_start; do { @@ -1165,6 +1152,75 @@ static void ndpi_int_tls_add_connection(struct ndpi_detection_module_struct *ndp /* **************************************** */ +static void checkExtensions(struct ndpi_flow_struct * const flow, int is_dtls, + u_int16_t extension_id, u_int16_t extension_len, u_int16_t extension_payload_offset) +{ + struct ndpi_packet_struct const * const packet = &flow->packet; + + if (extension_payload_offset + extension_len > packet->payload_packet_len) + { +#ifdef DEBUG_TLS + printf("[TLS] extension length exceeds remaining packet length: %u > %u.\n", + extension_len, packet->payload_packet_len - extension_payload_offset); +#endif + ndpi_set_risk(flow, NDPI_TLS_EXTENSION_SUSPICIOUS); + return; + } + + /* 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 + }; + size_t const allowed_non_iana_extensions_size = sizeof(allowed_non_iana_extensions) / + sizeof(allowed_non_iana_extensions[0]); + + /* see: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml */ + if (extension_id > 59 && extension_id != 65281) + { + u_int8_t extension_found = 0; + for (size_t i = 0; i < allowed_non_iana_extensions_size; ++i) + { + if (allowed_non_iana_extensions[i] == extension_id) + { + extension_found = 1; + break; + } + } + if (extension_found == 0) + { +#ifdef DEBUG_TLS + printf("[TLS] suspicious extension id: %u\n", extension_id); +#endif + ndpi_set_risk(flow, NDPI_TLS_EXTENSION_SUSPICIOUS); + return; + } + } + + /* Check for DTLS-only extensions. */ + if (is_dtls == 0) + { + if (extension_id == 53 || extension_id == 54) + { +#ifdef DEBUG_TLS + printf("[TLS] suspicious DTLS-only extension id: %u\n", extension_id); +#endif + ndpi_set_risk(flow, NDPI_TLS_EXTENSION_SUSPICIOUS); + return; + } + } +} + +/* **************************************** */ + int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow, uint32_t quic_version) { struct ndpi_packet_struct *packet = &flow->packet; @@ -1274,6 +1330,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, printf("TLS [server][extension_id: %u/0x%04X][len: %u]\n", extension_id, extension_id, extension_len); #endif + checkExtensions(flow, is_dtls, extension_id, extension_len, offset + 4); if(extension_id == 43 /* supported versions */) { if(extension_len >= 2) { @@ -1604,6 +1661,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #ifdef DEBUG_TLS printf("Client TLS [extension_id: %u][extension_len: %u]\n", extension_id, extension_len); #endif + checkExtensions(flow, is_dtls, extension_id, extension_len, offset + extension_offset); if((extension_id == 0) || (packet->payload[extn_off] != packet->payload[extn_off+1])) { /* Skip GREASE */ @@ -1641,6 +1699,11 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, #ifdef DEBUG_TLS printf("[TLS] SNI: [%s]\n", buffer); #endif + if (ndpi_is_printable_string(buffer, len) == 0) + { + ndpi_set_risk(flow, NDPI_TLS_EXTENSION_SUSPICIOUS); + } + if(!is_quic) { if(ndpi_match_hostname_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TLS, buffer, strlen(buffer))) flow->l4.tcp.tls.subprotocol_detected = 1; diff --git a/tests/result/tls_invalid_reads.pcap.out b/tests/result/tls_invalid_reads.pcap.out index 1362a78e7..2f2d92973 100644 --- a/tests/result/tls_invalid_reads.pcap.out +++ b/tests/result/tls_invalid_reads.pcap.out @@ -11,5 +11,5 @@ JA3 Host Stats: 1 TCP 192.168.10.101:3967 <-> 206.33.61.113:443 [proto: 91/TLS][cat: Web/5][4 pkts/330 bytes <-> 3 pkts/1497 bytes][Goodput ratio: 31/89][0.08 sec][bytes ratio: -0.639 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/38 25/19 58/38 24/19][Pkt Len c2s/s2c min/avg/max/stddev: 54/60 82/499 156/905 43/346][Risk: ** Obsolete TLS version (< 1.1) **][Risk Score: 50][TLSv1][JA3S: 53611273a714cb4789c8222932efd5a7 (INSECURE)][Cipher: TLS_RSA_WITH_RC4_128_MD5][Plen Bins: 0,0,0,33,0,0,0,0,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] - 2 TCP 10.191.139.17:58552 <-> 54.221.224.45:443 [VLAN: 2][proto: GTP:91.178/TLS.Amazon][cat: Web/5][2 pkts/442 bytes <-> 1 pkts/118 bytes][Goodput ratio: 41/0][0.23 sec][ALPN: ][TLSv1.2][Client: e.crashlytics.com][JA3C: 5f704e1e0a47641621b22177875f4e85][Firefox][PLAIN TEXT (e.crashlytics.com)][Plen Bins: 0,0,0,0,0,100,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] + 2 TCP 10.191.139.17:58552 <-> 54.221.224.45:443 [VLAN: 2][proto: GTP:91.178/TLS.Amazon][cat: Web/5][2 pkts/442 bytes <-> 1 pkts/118 bytes][Goodput ratio: 41/0][0.23 sec][ALPN: ][Risk: ** TLS extension suspicious **][Risk Score: 100][TLSv1.2][Client: e.crashlytics.com][JA3C: 5f704e1e0a47641621b22177875f4e85][Firefox][PLAIN TEXT (e.crashlytics.com)][Plen Bins: 0,0,0,0,0,100,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] 3 TCP 74.80.160.99:3258 -> 67.217.77.28:443 [proto: 91/TLS][cat: Web/5][1 pkts/64 bytes -> 0 pkts/0 bytes][Goodput ratio: 15/0][< 1 sec][Plen Bins: 100,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,0,0,0,0,0] |