aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/ndpiReader.c15
-rw-r--r--example/ndpi_util.c6
-rw-r--r--example/ndpi_util.h3
-rw-r--r--src/include/ndpi_includes.h1
-rw-r--r--src/include/ndpi_typedefs.h1
-rw-r--r--src/lib/protocols/ssl.c587
6 files changed, 489 insertions, 124 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c
index da64bdb21..216b4d9ab 100644
--- a/example/ndpiReader.c
+++ b/example/ndpiReader.c
@@ -800,10 +800,14 @@ static void printFlow(u_int16_t id, struct ndpi_flow_info *flow, u_int16_t threa
flow->dst2src_packets, (long long unsigned int) flow->dst2src_bytes);
if(flow->host_server_name[0] != '\0') fprintf(out, "[Host: %s]", flow->host_server_name);
+
if(flow->info[0] != '\0') fprintf(out, "[%s]", flow->info);
- if(flow->ssh_ssl.client_info[0] != '\0') fprintf(out, "[client: %s]", flow->ssh_ssl.client_info);
+
+ if(flow->ssh_ssl.ja3_client[0] != '\0') fprintf(out, "[JA3C: %s]", flow->ssh_ssl.ja3_client);
if(flow->ssh_ssl.server_info[0] != '\0') fprintf(out, "[server: %s]", flow->ssh_ssl.server_info);
+
+ if(flow->ssh_ssl.ja3_server[0] != '\0') fprintf(out, "[JA3S: %s]", flow->ssh_ssl.ja3_server);
if(flow->ssh_ssl.server_organization[0] != '\0') fprintf(out, "[organization: %s]", flow->ssh_ssl.server_organization);
if(flow->bittorent_hash[0] != '\0') fprintf(out, "[BT Hash: %s]", flow->bittorent_hash);
@@ -850,6 +854,15 @@ static void printFlow(u_int16_t id, struct ndpi_flow_info *flow, u_int16_t threa
if((flow->ssh_ssl.client_info[0] != '\0') || (flow->ssh_ssl.server_info[0] != '\0')) {
json_object *sjObj = json_object_new_object();
+ if(flow->ssh_ssl.ja3_server[0] != '\0')
+ json_object_object_add(jObj,"ja3s",json_object_new_string(flow->ssh_ssl.ja3_server));
+
+ if(flow->ssh_ssl.ja3_client[0] != '\0')
+ json_object_object_add(jObj,"ja3c",json_object_new_string(flow->ssh_ssl.ja3_client));
+
+ if(flow->ja3_server[0] != '\0')
+ json_object_object_add(jObj,"host.server.ja3",json_object_new_string(flow->ja3_server));
+
if(flow->ssh_ssl.client_info[0] != '\0')
json_object_object_add(sjObj, "client", json_object_new_string(flow->ssh_ssl.client_info));
diff --git a/example/ndpi_util.c b/example/ndpi_util.c
index 9e1e72132..174312cb8 100644
--- a/example/ndpi_util.c
+++ b/example/ndpi_util.c
@@ -567,7 +567,11 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s",
flow->ndpi_flow->protos.stun_ssl.ssl.server_certificate);
snprintf(flow->ssh_ssl.server_organization, sizeof(flow->ssh_ssl.server_organization), "%s",
- flow->ndpi_flow->protos.stun_ssl.ssl.server_organization);
+ flow->ndpi_flow->protos.stun_ssl.ssl.server_organization);
+ snprintf(flow->ssh_ssl.ja3_client, sizeof(flow->ssh_ssl.ja3_client), "%s",
+ flow->ndpi_flow->protos.stun_ssl.ssl.ja3_client);
+ snprintf(flow->ssh_ssl.ja3_server, sizeof(flow->ssh_ssl.ja3_server), "%s",
+ flow->ndpi_flow->protos.stun_ssl.ssl.ja3_server);
}
}
diff --git a/example/ndpi_util.h b/example/ndpi_util.h
index 0a5a3b8c2..57772455f 100644
--- a/example/ndpi_util.h
+++ b/example/ndpi_util.h
@@ -97,7 +97,8 @@ typedef struct ndpi_flow_info {
char bittorent_hash[41];
struct {
- char client_info[64], server_info[64], server_organization[64];
+ char client_info[64], server_info[64], server_organization[64],
+ ja3_client[33], ja3_server[33];
} ssh_ssl;
void *src_id, *dst_id;
diff --git a/src/include/ndpi_includes.h b/src/include/ndpi_includes.h
index f77f8cfc4..f8bde5194 100644
--- a/src/include/ndpi_includes.h
+++ b/src/include/ndpi_includes.h
@@ -27,6 +27,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h
index 97fb71a80..77f0e0e85 100644
--- a/src/include/ndpi_typedefs.h
+++ b/src/include/ndpi_typedefs.h
@@ -1092,6 +1092,7 @@ struct ndpi_flow_struct {
struct {
struct {
char client_certificate[64], server_certificate[64], server_organization[64];
+ char ja3_client[33], ja3_server[33];
} ssl;
struct {
diff --git a/src/lib/protocols/ssl.c b/src/lib/protocols/ssl.c
index 05988a8d4..9bd5df88c 100644
--- a/src/lib/protocols/ssl.c
+++ b/src/lib/protocols/ssl.c
@@ -27,7 +27,7 @@
#include "ndpi_api.h"
-// #define CERTIFICATE_DEBUG 1
+/* #define CERTIFICATE_DEBUG 1 */
#define NDPI_MAX_SSL_REQUEST_SIZE 10000
@@ -35,6 +35,202 @@
extern u_int8_t is_skype_flow(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow);
+/* **************************************** */
+
+typedef struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+} MD5_CTX;
+
+static int is_big_endian(void) {
+ static const int n = 1;
+ return ((char *) &n)[0] == 0;
+}
+
+static void byteReverse(unsigned char *buf, unsigned longs) {
+ uint32_t t;
+
+ // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
+ if (is_big_endian()) {
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ * (uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+ }
+}
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+// initialization constants.
+static void MD5Init(MD5_CTX *ctx) {
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
+ uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
+ uint32_t t;
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++;
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f;
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ memcpy(ctx->in, buf, len);
+}
+
+static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
+ unsigned count;
+ unsigned char *p;
+ uint32_t *c = (uint32_t*)ctx->in;
+
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ p = ctx->in + count;
+ *p++ = 0x80;
+ count = 64 - 1 - count;
+ if (count < 8) {
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ memset(ctx->in, 0, 56);
+ } else {
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ c[14] = ctx->bits[0];
+ c[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset((char *) ctx, 0, sizeof(*ctx));
+}
+
+/* **************************************** */
+
static u_int32_t ndpi_ssl_refine_master_protocol(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow, u_int32_t protocol)
{
@@ -143,21 +339,42 @@ static void stripCertificateTrailer(char *buffer, int buffer_len) {
}
}
-/* Code fixes courtesy of Alexsandro Brahm <alex@digistar.com.br> */
+/* https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967 */
+
+#define JA3_STR_LEN 256
+#define MAX_NUM_JA3 24
+
+struct ja3_info {
+ u_int16_t ssl_version;
+ u_int16_t num_cipher, cipher[MAX_NUM_JA3];
+ u_int16_t num_ssl_extension, ssl_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;
+};
+
+/* code fixes courtesy of Alexsandro Brahm <alex@digistar.com.br> */
int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
char *buffer, int buffer_len) {
struct ndpi_packet_struct *packet = &flow->packet;
+ struct ja3_info ja3;
+ u_int16_t ssl_version = (packet->payload[1] << 8) + packet->payload[2], ja3_str_len;
+ char ja3_str[JA3_STR_LEN];
+ MD5_CTX ctx;
+ u_char md5_hash[16];
+
+ memset(&ja3, 0, sizeof(ja3));
#ifdef CERTIFICATE_DEBUG
{
- u_int16_t ssl_version = (packet->payload[1] << 8) + packet->payload[2];
- u_int16_t ssl_len = (packet->payload[3] << 8) + packet->payload[4];
-
- printf("SSL Record [version: 0x%02X][len: %u]\n", ssl_version, ssl_len);
+ u_int16_t ssl_len = (packet->payload[3] << 8) + packet->payload[4];
+
+ printf("SSL Record [version: %u][len: %u]\n", ssl_version, ssl_len);
}
#endif
+ ja3.ssl_version = ssl_version;
+
/*
Nothing matched so far: let's decode the certificate with some heuristics
Patches courtesy of Denys Fedoryshchenko <nuclearcat@nuclearcat.com>
@@ -184,9 +401,62 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct,
|| (handshake_protocol == 0xb) /* Server Hello and Certificate message types are interesting for us */) {
u_int num_found = 0;
- if(handshake_protocol == 0x02)
+ if(handshake_protocol == 0x02) {
+ u_int16_t offset = 43, extension_len;
+ u_int8_t session_id_len = packet->payload[43];
+
+ offset += session_id_len+1;
+
+ ja3.num_cipher = 1, ja3.cipher[0] = ntohs(*((u_int16_t*)&packet->payload[offset]));
+
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [server][session_id_len: %u][cipher: %04X]\n", session_id_len, ja3.cipher[0]);
+#endif
+
+ offset += 2 + 1;
+ extension_len = ntohs(*((u_int16_t*)&packet->payload[offset]));
+
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [server][extension_len: %u]\n", extension_len);
+#endif
+ offset += 2;
+
+ for(i=0; i<extension_len; ) {
+ u_int16_t id, len;
+
+ if(offset >= (packet->payload_packet_len+4)) break;
+
+ id = ntohs(*((u_int16_t*)&packet->payload[offset]));
+ len = ntohs(*((u_int16_t*)&packet->payload[offset+2]));
+
+ if(ja3.num_ssl_extension < MAX_NUM_JA3)
+ ja3.ssl_extension[ja3.num_ssl_extension++] = id;
+
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [server][extension_id: %u]\n", id);
+#endif
+
+ i += 4 + len, offset += 4 + len;
+ }
+
+ ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.ssl_version);
+
+ for(i=0; i<ja3.num_cipher; i++)
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.cipher[i]);
+
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ",");
+
+ /* ********** */
+
+ for(i=0; i<ja3.num_ssl_extension; i++)
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.ssl_extension[i]);
+
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [server] %s\n", ja3_str);
+#endif
+
flow->l4.tcp.ssl_seen_server_cert = 1;
- else
+ } else
flow->l4.tcp.ssl_seen_certificate = 1;
/* Check after handshake protocol header (5 bytes) and message header (4 bytes) */
@@ -235,6 +505,17 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct,
snprintf(flow->protos.stun_ssl.ssl.server_certificate,
sizeof(flow->protos.stun_ssl.ssl.server_certificate), "%s", buffer);
}
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str));
+ MD5Final(md5_hash, &ctx);
+
+ for(i=0, j=0; i<16; i++)
+ j += snprintf(&flow->protos.stun_ssl.ssl.ja3_server[j],
+ sizeof(flow->protos.stun_ssl.ssl.ja3_server)-j, "%02x", md5_hash[i]);
+
+ printf("[JA3] Server: %s \n", flow->protos.stun_ssl.ssl.ja3_server);
+
return(1 /* Server Certificate */);
}
}
@@ -242,101 +523,164 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct,
}
} else if(handshake_protocol == 0x01 /* Client Hello */) {
u_int offset, base_offset = 43;
+
if(base_offset + 2 <= packet->payload_packet_len) {
- u_int16_t session_id_len = packet->payload[base_offset];
+ u_int16_t session_id_len = packet->payload[base_offset];
- if((session_id_len+base_offset+2) <= total_len) {
- u_int16_t cypher_len = packet->payload[session_id_len+base_offset+2] + (packet->payload[session_id_len+base_offset+1] << 8);
- offset = base_offset + session_id_len + cypher_len + 2;
+ if((session_id_len+base_offset+2) <= total_len) {
+ u_int16_t cypher_len = packet->payload[session_id_len+base_offset+2] + (packet->payload[session_id_len+base_offset+1] << 8);
+ u_int16_t i, cypher_offset = base_offset + session_id_len + 3;
- flow->l4.tcp.ssl_seen_client_cert = 1;
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [client cypher_len: %u]\n", cypher_len);
+#endif
- if(offset < total_len) {
- u_int16_t compression_len;
- u_int16_t extensions_len;
+ for(i=0; i<cypher_len; i++) {
+ u_int16_t *id = (u_int16_t*)&packet->payload[cypher_offset+i];
- offset++;
- compression_len = packet->payload[offset];
- offset++;
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [cypher suite: %u]\n", ntohs(*id));
+#endif
+
+ if(ja3.num_cipher < MAX_NUM_JA3)
+ ja3.cipher[ja3.num_cipher++] = ntohs(*id);
+
+ i++;
+ }
+
+ offset = base_offset + session_id_len + cypher_len + 2;
+
+ flow->l4.tcp.ssl_seen_client_cert = 1;
+
+ if(offset < total_len) {
+ u_int16_t compression_len;
+ u_int16_t extensions_len;
+
+ offset++;
+ compression_len = packet->payload[offset];
+ offset++;
#ifdef CERTIFICATE_DEBUG
- printf("SSL [compression_len: %u]\n", compression_len);
+ printf("SSL [compression_len: %u]\n", compression_len);
#endif
- // offset += compression_len + 3;
- offset += compression_len;
+ // offset += compression_len + 3;
+ offset += compression_len;
- if(offset < total_len) {
- extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset]));
- offset += 2;
+ if(offset < total_len) {
+ extensions_len = ntohs(*((u_int16_t*)&packet->payload[offset]));
+ offset += 2;
#ifdef CERTIFICATE_DEBUG
- printf("SSL [extensions_len: %u]\n", extensions_len);
+ printf("SSL [extensions_len: %u]\n", extensions_len);
#endif
- if((extensions_len+offset) <= total_len) {
- /* Move to the first extension
- Type is u_int to avoid possible overflow on extension_len addition */
- u_int extension_offset = 0;
+ if((extensions_len+offset) <= total_len) {
+ /* Move to the first extension
+ Type is u_int to avoid possible overflow on extension_len addition */
+ u_int extension_offset = 0;
+ u_int32_t md5h[4], j;
- while(extension_offset < extensions_len) {
- u_int16_t extension_id, extension_len;
+ while(extension_offset < extensions_len) {
+ u_int16_t extension_id, extension_len;
- extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset]));
- extension_offset += 2;
+ extension_id = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset]));
+ extension_offset += 2;
- extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset]));
- extension_offset += 2;
+ extension_len = ntohs(*((u_int16_t*)&packet->payload[offset+extension_offset]));
+ extension_offset += 2;
#ifdef CERTIFICATE_DEBUG
- printf("SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len);
+ printf("SSL [extension_id: %u][extension_len: %u]\n", extension_id, extension_len);
#endif
- if(extension_id == 0) {
-#if 1
- u_int16_t len;
-
- len = (packet->payload[offset+extension_offset+3] << 8) + packet->payload[offset+extension_offset+4];
- len = (u_int)ndpi_min(len, buffer_len-1);
- strncpy(buffer, (char*)&packet->payload[offset+extension_offset+5], len);
- buffer[len] = '\0';
-#else
- /* old code */
- u_int begin = 0;
- char *server_name = (char*)&packet->payload[offset+extension_offset];
-
- while(begin < extension_len) {
- if((!ndpi_isprint(server_name[begin]))
- || ndpi_ispunct(server_name[begin])
- || ndpi_isspace(server_name[begin]))
- begin++;
- else
- break;
- }
-
- len = (u_int)ndpi_min(extension_len-begin, buffer_len-1);
- strncpy(buffer, &server_name[begin], len);
- buffer[len] = '\0';
-#endif
-
- stripCertificateTrailer(buffer, buffer_len);
+ if(ja3.num_ssl_extension < MAX_NUM_JA3)
+ ja3.ssl_extension[ja3.num_ssl_extension++] = extension_id;
+
+ if(extension_id == 0 /* server name */) {
+ u_int16_t len;
+
+ len = (packet->payload[offset+extension_offset+3] << 8) + packet->payload[offset+extension_offset+4];
+ len = (u_int)ndpi_min(len, buffer_len-1);
+ strncpy(buffer, (char*)&packet->payload[offset+extension_offset+5], len);
+ buffer[len] = '\0';
- if(!ndpi_struct->disable_metadata_export) {
- snprintf(flow->protos.stun_ssl.ssl.client_certificate,
- sizeof(flow->protos.stun_ssl.ssl.client_certificate), "%s", buffer);
- }
+ stripCertificateTrailer(buffer, buffer_len);
- /* We're happy now */
- return(2 /* Client Certificate */);
+ if(!ndpi_struct->disable_metadata_export) {
+ snprintf(flow->protos.stun_ssl.ssl.client_certificate,
+ sizeof(flow->protos.stun_ssl.ssl.client_certificate), "%s", buffer);
}
+ } else if(extension_id == 10 /* supported groups */) {
+ u_int16_t i, s_offset = offset+extension_offset + 2;
+
+ for(i=0; i<extension_len; i++) {
+ u_int16_t s_group = ntohs(*((u_int16_t*)&packet->payload[s_offset+i]));
+
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [EllipticCurve: %u]\n", s_group);
+#endif
- extension_offset += extension_len;
+ if(ja3.num_elliptic_curve < MAX_NUM_JA3)
+ ja3.elliptic_curve[ja3.num_elliptic_curve++] = s_group;
+
+ i++;
+ }
+ } else if(extension_id == 11 /* ec_point_formats groups */) {
+#ifdef CERTIFICATE_DEBUG
+ printf("SSL [EllipticCurveFormat: %u]\n", packet->payload[offset+extension_offset+1]);
+#endif
+ ja3.elliptic_curve_point_format = packet->payload[offset+extension_offset+1],
+ ja3.num_elliptic_curve_point_format = 1;
}
+
+ extension_offset += extension_len;
+
+#ifdef CERTIFICATE_DEBUG
+ // printf("SSL [extension_offset/len: %u/%u]\n", extension_offset, extension_len);
+#endif
}
+
+ ja3_str_len = snprintf(ja3_str, sizeof(ja3_str), "%u,", ja3.ssl_version);
+
+ for(i=0; i<ja3.num_cipher; i++)
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.cipher[i]);
+
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ",");
+
+ /* ********** */
+
+ for(i=0; i<ja3.num_ssl_extension; i++)
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.ssl_extension[i]);
+
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ",");
+
+ /* ********** */
+
+ for(i=0; i<ja3.num_elliptic_curve; i++)
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%s%u", (i > 0) ? "-" : "", ja3.elliptic_curve[i]);
+
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, ",");
+
+ if(ja3.num_elliptic_curve_point_format)
+ ja3_str_len += snprintf(&ja3_str[ja3_str_len], sizeof(ja3_str)-ja3_str_len, "%u", ja3.elliptic_curve_point_format);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (const unsigned char *)ja3_str, strlen(ja3_str));
+ MD5Final(md5_hash, &ctx);
+
+ for(i=0, j=0; i<16; i++)
+ j += snprintf(&flow->protos.stun_ssl.ssl.ja3_client[j],
+ sizeof(flow->protos.stun_ssl.ssl.ja3_client)-j, "%02x", md5_hash[i]);
+
+ printf("[JA3] Client: %s \n", flow->protos.stun_ssl.ssl.ja3_client);
+
+ return(2 /* Client Certificate */);
}
}
}
}
+ }
}
}
}
@@ -345,69 +689,70 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct,
}
void getSSLorganization(struct ndpi_detection_module_struct *ndpi_struct,
- struct ndpi_flow_struct *flow,
- char *buffer, int buffer_len) {
- struct ndpi_packet_struct *packet = &flow->packet;
+ struct ndpi_flow_struct *flow,
+ char *buffer, int buffer_len) {
+ struct ndpi_packet_struct *packet = &flow->packet;
- if(packet->payload[0] != 0x16 /* Handshake */)
- return;
+ if(packet->payload[0] != 0x16 /* Handshake */)
+ return;
- u_int16_t total_len = (packet->payload[3] << 8) + packet->payload[4] + 5 /* SSL Header */;
- u_int8_t handshake_protocol = packet->payload[5]; /* handshake protocol a bit misleading, it is message type according TLS specs */
+ u_int16_t total_len = (packet->payload[3] << 8) + packet->payload[4] + 5 /* SSL Header */;
+ u_int8_t handshake_protocol = packet->payload[5]; /* handshake protocol a bit misleading, it is message type according TLS specs */
- if(handshake_protocol != 0x02 && handshake_protocol != 0xb /* Server Hello and Certificate message types are interesting for us */)
- return;
+ if(handshake_protocol != 0x02 && handshake_protocol != 0xb /* Server Hello and Certificate message types are interesting for us */)
+ return;
- /* Truncate total len, search at least in incomplete packet */
- if(total_len > packet->payload_packet_len)
- total_len = packet->payload_packet_len;
+ /* Truncate total len, search at least in incomplete packet */
+ if(total_len > packet->payload_packet_len)
+ total_len = packet->payload_packet_len;
- memset(buffer, 0, buffer_len);
+ memset(buffer, 0, buffer_len);
- /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */
- u_int num_found = 0;
- u_int i, j;
- for(i = 9; i < packet->payload_packet_len-4; i++) {
- /* Organization OID: 2.5.4.10 */
- if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x0a)) {
- u_int8_t type_tag = packet->payload[i+3]; // 0x0c: utf8string / 0x13: printable_string
- u_int8_t server_len = packet->payload[i+4];
-
- num_found++;
- /* what we want is subject certificate, so we bypass the issuer certificate */
- if(num_found != 2) continue;
-
- // packet is truncated... further inspection is not needed
- if(i+4+server_len >= packet->payload_packet_len) {
- break;
- }
+ /* Check after handshake protocol header (5 bytes) and message header (4 bytes) */
+ u_int num_found = 0;
+ u_int i, j;
+ for(i = 9; i < packet->payload_packet_len-4; i++) {
+ /* Organization OID: 2.5.4.10 */
+ if((packet->payload[i] == 0x55) && (packet->payload[i+1] == 0x04) && (packet->payload[i+2] == 0x0a)) {
+ u_int8_t type_tag = packet->payload[i+3]; // 0x0c: utf8string / 0x13: printable_string
+ u_int8_t server_len = packet->payload[i+4];
+
+ num_found++;
+ /* what we want is subject certificate, so we bypass the issuer certificate */
+ if(num_found != 2) continue;
+
+ // packet is truncated... further inspection is not needed
+ if(i+4+server_len >= packet->payload_packet_len) {
+ break;
+ }
- char *server_org = (char*)&packet->payload[i+5];
+ char *server_org = (char*)&packet->payload[i+5];
- u_int len = (u_int)ndpi_min(server_len, buffer_len-1);
- strncpy(buffer, server_org, len);
- buffer[len] = '\0';
+ u_int len = (u_int)ndpi_min(server_len, buffer_len-1);
+ strncpy(buffer, server_org, len);
+ buffer[len] = '\0';
- // check if organization string are all printable
- u_int8_t is_printable = 1;
- for (j = 0; j < len; j++) {
- if(!ndpi_isprint(buffer[j])) {
- is_printable = 0;
- break;
- }
- }
+ // check if organization string are all printable
+ u_int8_t is_printable = 1;
+ for (j = 0; j < len; j++) {
+ if(!ndpi_isprint(buffer[j])) {
+ is_printable = 0;
+ break;
+ }
+ }
- if(is_printable == 1) {
- snprintf(flow->protos.stun_ssl.ssl.server_organization,
- sizeof(flow->protos.stun_ssl.ssl.server_organization), "%s", buffer);
+ if(is_printable == 1) {
+ snprintf(flow->protos.stun_ssl.ssl.server_organization,
+ sizeof(flow->protos.stun_ssl.ssl.server_organization), "%s", buffer);
#ifdef CERTIFICATE_DEBUG
- printf("Certificate origanization: %s\n", flow->protos.stun_ssl.ssl.server_organization);
+ printf("Certificate origanization: %s\n", flow->protos.stun_ssl.ssl.server_organization);
#endif
- }
- }
+ }
}
+ }
}
+
int sslTryAndRetrieveServerCertificate(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &flow->packet;
@@ -745,7 +1090,7 @@ void ndpi_search_ssl_tcp(struct ndpi_detection_module_struct *ndpi_struct, struc
/* No whatsapp, let's try SSL */
if(sslDetectProtocolFromCertificate(ndpi_struct, flow) > 0)
return;
- }
+ }
if(packet->payload_packet_len > 40 && flow->l4.tcp.ssl_stage == 0) {
NDPI_LOG_DBG2(ndpi_struct, "first ssl packet\n");