aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2021-07-19 13:58:22 +0200
committerToni Uhlig <matzeton@googlemail.com>2021-07-19 16:05:51 +0200
commit370ea972c127c6a8ed704f8ca237aae6e9eb2660 (patch)
tree6c71a429d37be7ed0e8be939702cd91545266b8d
parentb95bd0358fd43d9fdfdc5266e3c8923b91e1d4db (diff)
Added risk: TLS_EXTENSION_SUSPICIOUSadded/sus_tls_ext_risk
* validates client/server hello TLS extensions * inspects content for some extensions Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r--src/include/ndpi_main.h2
-rw-r--r--src/include/ndpi_typedefs.h1
-rw-r--r--src/include/ndpi_utils.h13
-rw-r--r--src/lib/ndpi_main.c1
-rw-r--r--src/lib/ndpi_utils.c48
-rw-r--r--src/lib/protocols/tls.c99
-rw-r--r--tests/result/tls_invalid_reads.pcap.out2
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]