Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STUN: add dissection of DTLS handshake #2018

Merged
merged 1 commit into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/include/ndpi_typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,7 @@ struct ndpi_detection_module_struct {
int opportunistic_tls_imap_enabled;
int opportunistic_tls_pop_enabled;
int opportunistic_tls_ftp_enabled;
int opportunistic_tls_stun_enabled;

u_int32_t monitoring_stun_pkts_to_process;
u_int32_t monitoring_stun_flags;
Expand Down Expand Up @@ -1466,8 +1467,7 @@ struct ndpi_flow_struct {
} kerberos_buf;

struct {
u_int8_t num_pkts, num_binding_requests;
u_int16_t num_processed_pkts;
u_int8_t num_pkts, num_binding_requests, num_processed_pkts, maybe_dtls;
} stun;

struct {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/ndpi_content_match.c.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,10 @@ static ndpi_tls_cert_name_match tls_certificate_match [] = {
{ "O=Riot Games, Inc.", NDPI_PROTOCOL_RIOTGAMES },
{ "O=Riot Games Inc", NDPI_PROTOCOL_RIOTGAMES },

{ "CN=hangouts", NDPI_PROTOCOL_HANGOUT_DUO },
{ "CN=Snapchat Inc.", NDPI_PROTOCOL_SNAPCHAT_CALL },
{ "CN=NVIDIA GameStream", NDPI_PROTOCOL_GEFORCENOW },

{ NULL, 0 }
};

Expand Down
6 changes: 6 additions & 0 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3004,6 +3004,7 @@ struct ndpi_detection_module_struct *ndpi_init_detection_module(ndpi_init_prefs
ndpi_str->opportunistic_tls_imap_enabled = 1;
ndpi_str->opportunistic_tls_pop_enabled = 1;
ndpi_str->opportunistic_tls_ftp_enabled = 1;
ndpi_str->opportunistic_tls_stun_enabled = 1;

ndpi_str->monitoring_stun_pkts_to_process = 4;
ndpi_str->monitoring_stun_flags = 0;
Expand Down Expand Up @@ -9846,6 +9847,9 @@ int ndpi_set_opportunistic_tls(struct ndpi_detection_module_struct *ndpi_struct,
case NDPI_PROTOCOL_FTP_CONTROL:
ndpi_struct->opportunistic_tls_ftp_enabled = value;
return 0;
case NDPI_PROTOCOL_STUN:
ndpi_struct->opportunistic_tls_stun_enabled = value;
return 0;
default:
return -1;
}
Expand All @@ -9868,6 +9872,8 @@ int ndpi_get_opportunistic_tls(struct ndpi_detection_module_struct *ndpi_struct,
return ndpi_struct->opportunistic_tls_pop_enabled;
case NDPI_PROTOCOL_FTP_CONTROL:
return ndpi_struct->opportunistic_tls_ftp_enabled;
case NDPI_PROTOCOL_STUN:
return ndpi_struct->opportunistic_tls_stun_enabled;
default:
return -1;
}
Expand Down
53 changes: 30 additions & 23 deletions src/lib/protocols/stun.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@

#define STUN_HDR_LEN 20 /* STUN message header length, Classic-STUN (RFC 3489) and STUN (RFC 8489) both */

extern void switch_to_tls(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow);
extern int is_dtls(const u_int8_t *buf, u_int32_t buf_len, u_int32_t *block_len);

static int stun_monitoring(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
Expand Down Expand Up @@ -212,6 +216,7 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *
u_int16_t *app_proto) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
u_int16_t msg_type, msg_len;
u_int32_t unused;
int rc;

if(packet->iph &&
Expand All @@ -221,6 +226,31 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *
return(NDPI_IS_NOT_STUN);
}

/* If we're here it's because this does not look like STUN anymore
as this was a flow that started as STUN and turned into something
else. Let's investigate what is that about */
if(flow->stun.num_pkts > 0 && is_dtls(payload, payload_length, &unused)) {
#ifdef DEBUG_STUN
printf("[STUN] DTLS?\n");
#endif
/* Switching to TLS dissector is tricky, because we are calling one dissector
from another one, and that is not a common operation...
Additionally:
* at that point protocol stack is still empty
* we have room for only two protocols in flow->detected_protocol_stack[] so
we can't have something like STUN/DTLS/SNAPCHAT_CALL
* the easiest solution is skipping STUN, and let TLS dissector to set both
master (i.e. DTLS) and subprotocol (if any) */
if(ndpi_struct->opportunistic_tls_stun_enabled) {
flow->stun.maybe_dtls = 1;
switch_to_tls(ndpi_struct, flow);
}
/* We don't want to mess up with TLS classification/results but we don't want to
exclude STUN right away to keep trying it in the case that this packet is
not a real DTLS one */
return(NDPI_IS_NOT_STUN);
}

if(payload_length < STUN_HDR_LEN) {
/* This looks like an invalid packet */

Expand Down Expand Up @@ -260,29 +290,6 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *
#ifdef DEBUG_STUN
printf("[STUN] msg_type = %04X\n", msg_type);
#endif

/*
If we're here it's because this does not look like STUN anymore
as this was a flow that started as STUN and turned into something
else. Let's investigate what is that about
*/
if(payload[0] == 0x16) {
/* Let's check if this is DTLS used by some socials */
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
u_int16_t total_len, version = htons(*((u_int16_t*) &packet->payload[1]));

switch (version) {
case 0xFEFF: /* DTLS 1.0 */
case 0xFEFD: /* DTLS 1.2 */
total_len = ntohs(*((u_int16_t*) &packet->payload[11])) + 13;

if(payload_length == total_len) {
flow->guessed_protocol_id = NDPI_PROTOCOL_DTLS;
return(NDPI_IS_NOT_STUN);
}
}
}

return(NDPI_IS_NOT_STUN);
}

Expand Down
85 changes: 65 additions & 20 deletions src/lib/protocols/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
extern char *strptime(const char *s, const char *format, struct tm *tm);
extern int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow, uint32_t quic_version);
static void ndpi_search_tls_wrapper(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow);
extern int http_process_user_agent(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
const u_int8_t *ua_ptr, u_int16_t ua_ptr_len);
Expand Down Expand Up @@ -1188,6 +1190,36 @@ static int ndpi_search_tls_tcp(struct ndpi_detection_module_struct *ndpi_struct,

/* **************************************** */

int is_dtls(const u_int8_t *buf, u_int32_t buf_len, u_int32_t *block_len) {
if(buf_len <= 13)
return 0;

if((buf[0] != 0x16 && buf[0] != 0x14 && buf[0] != 0x17) || /* Handshake, change-cipher-spec, Application-Data */
!((buf[1] == 0xfe && buf[2] == 0xff) || /* Versions */
(buf[1] == 0xfe && buf[2] == 0xfd) ||
(buf[1] == 0x01 && buf[2] == 0x00))) {
#ifdef DEBUG_TLS
printf("[TLS] DTLS invalid block 0x%x or old version 0x%x-0x%x-0x%x\n",
buf[0], buf[1], buf[2], buf[3]);
#endif
return 0;
}
*block_len = ntohs(*((u_int16_t*)&buf[11]));
#ifdef DEBUG_TLS
printf("[TLS] DTLS block len: %d\n", *block_len);
#endif
if(*block_len == 0 || (*block_len + 12 >= buf_len)) { /* We might have multiple DTLS records */
#ifdef DEBUG_TLS
printf("[TLS] DTLS invalid block len %d (buf_len %d)\n",
*block_len, buf_len);
#endif
return 0;
}
return 1;
}

/* **************************************** */

static int ndpi_search_tls_udp(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
Expand All @@ -1210,29 +1242,23 @@ static int ndpi_search_tls_udp(struct ndpi_detection_module_struct *ndpi_struct,
u_int32_t block_len;
const u_int8_t *block = (const u_int8_t *)&p[processed];

if((block[0] != 0x16 && block[0] != 0x14 && block[0] != 0x17) || /* Handshake, change-cipher-spec, Application-Data */
!((block[1] == 0xfe && block[2] == 0xff) ||
(block[1] == 0xfe && block[2] == 0xfd) ||
(block[1] == 0x01 && block[2] == 0x00))) {
#ifdef DEBUG_TLS
printf("[TLS] DTLS invalid block 0x%x or old version 0x%x-0x%x-0x%x\n",
block[0], block[1], block[2], block[3]);
#endif
no_dtls = 1;
break;
}
block_len = ntohs(*((u_int16_t*)&block[11]));
if(!is_dtls(block, p_len, &block_len)) {
if(processed == 0 && /* First block */
flow->stun.maybe_dtls == 1) {
/* Sometimes STUN packets are interleaved with TLS ones. Ignore STUN ones
since we already are after STUN dissection and we are interested only on
TLS stuff right now */
#ifdef DEBUG_TLS
printf("[TLS] DTLS block len: %d\n", block_len);
#endif
if(block_len == 0 || (processed + block_len + 12 >= p_len)) {
#ifdef DEBUG_TLS
printf("[TLS] DTLS invalid block len %d (processed %d, p_len %d)\n",
block_len, processed, p_len);
printf("Probably a stun packet. Keep going with TLS on next packets\n");
#endif
/* Note that we can immediately "return" because, being the first block,
we don't need to restore packet->payload and packet->payload_packet_len */
return(1); /* Keep working */
}
no_dtls = 1;
break;
}

/* We process only handshake msgs */
if(block[0] == 0x16) {
if(processed + block_len + 13 > p_len) {
Expand Down Expand Up @@ -1358,8 +1384,9 @@ static void tlsInitExtraPacketProcessing(struct ndpi_detection_module_struct *nd
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;

/* At most 12 packets should almost always be enough to find the server certificate if it's there */
flow->max_extra_packets_to_check = 12 + (ndpi_struct->num_tls_blocks_to_follow*4);
/* At most 12 packets should almost always be enough to find the server certificate if it's there.
Exception: DTLS traffic with fragments, retransmissions and STUN packets */
flow->max_extra_packets_to_check = ((packet->udp != NULL) ? 20 : 12) + (ndpi_struct->num_tls_blocks_to_follow*4);
flow->extra_packets_func = (packet->udp != NULL) ? ndpi_search_tls_udp : ndpi_search_tls_tcp;
}

Expand All @@ -1385,6 +1412,24 @@ void switch_extra_dissection_to_tls(struct ndpi_detection_module_struct *ndpi_st

/* **************************************** */

void switch_to_tls(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
#ifdef DEBUG_TLS
printf("Switching to TLS\n");
#endif

/* Reset reassemblers */
if(flow->tls_quic.message[0].buffer)
ndpi_free(flow->tls_quic.message[0].buffer);
memset(&flow->tls_quic.message[0], '\0', sizeof(flow->tls_quic.message[0]));
if(flow->tls_quic.message[1].buffer)
ndpi_free(flow->tls_quic.message[1].buffer);
memset(&flow->tls_quic.message[1], '\0', sizeof(flow->tls_quic.message[1]));

ndpi_search_tls_wrapper(ndpi_struct, flow);
}

static void tls_subclassify_by_alpn(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
/* Right now we have only one rule so we can keep it trivial */
Expand Down
17 changes: 11 additions & 6 deletions tests/cfgs/default/result/stun.pcap.out
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
Guessed flow protos: 0

DPI Packets (TCP): 7 (3.50 pkts/flow)
DPI Packets (UDP): 17 (5.67 pkts/flow)
DPI Packets (UDP): 18 (6.00 pkts/flow)
Confidence DPI : 5 (flows)
Num dissector calls: 595 (119.00 diss/flow)
Num dissector calls: 578 (115.60 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/3/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
LRU cache stun: 6/38/0 (insert/search/found)
LRU cache tls_cert: 0/0/0 (insert/search/found)
LRU cache stun: 4/34/0 (insert/search/found)
LRU cache tls_cert: 1/2/0 (insert/search/found)
LRU cache mining: 0/0/0 (insert/search/found)
LRU cache msteams: 0/0/0 (insert/search/found)
LRU cache stun_zoom: 0/0/0 (insert/search/found)
Automa host: 0/0 (search/found)
Automa domain: 0/0 (search/found)
Automa tls cert: 0/0 (search/found)
Automa tls cert: 1/1 (search/found)
Automa risk mask: 1/0 (search/found)
Automa common alpns: 0/0 (search/found)
Patricia risk mask: 8/0 (search/found)
Expand All @@ -26,8 +26,13 @@ STUN 62 7620 2
GoogleHangoutDuo 33 6292 1
FacebookVoip 75 10554 1

JA3 Host Stats:
IP Address # JA3C
1 192.168.12.169 1


1 UDP 192.168.12.169:38123 <-> 31.13.86.54:40003 [proto: 78.268/STUN.FacebookVoip][IP: 119/Facebook][ClearText][Confidence: DPI][DPI packets: 2][cat: VoIP/10][40 pkts/6134 bytes <-> 35 pkts/4420 bytes][Goodput ratio: 73/67][10.09 sec][Hostname/SNI: turner.facebook][bytes ratio: 0.162 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 260/331 6004/5997 1040/1126][Pkt Len c2s/s2c min/avg/max/stddev: 70/68 153/126 190/174 31/39][Risk: ** Known Proto on Non Std Port **][Risk Score: 50][PLAIN TEXT (unauthorized)][Plen Bins: 8,14,9,28,40,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]
2 UDP 192.168.12.169:49153 <-> 142.250.82.99:3478 [proto: 78.201/STUN.GoogleHangoutDuo][IP: 126/Google][ClearText][Confidence: DPI][DPI packets: 4][cat: VoIP/10][18 pkts/2856 bytes <-> 15 pkts/3436 bytes][Goodput ratio: 74/82][2.12 sec][bytes ratio: -0.092 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 8/0 88/153 699/625 177/222][Pkt Len c2s/s2c min/avg/max/stddev: 107/76 159/229 588/1240 107/297][PLAIN TEXT (BwlkYDtFJ)][Plen Bins: 0,6,57,21,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0]
2 UDP 192.168.12.169:49153 <-> 142.250.82.99:3478 [proto: 30.201/DTLS.GoogleHangoutDuo][IP: 126/Google][Encrypted][Confidence: DPI][DPI packets: 5][cat: VoIP/10][18 pkts/2856 bytes <-> 15 pkts/3436 bytes][Goodput ratio: 74/82][2.12 sec][bytes ratio: -0.092 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 8/0 88/153 699/625 177/222][Pkt Len c2s/s2c min/avg/max/stddev: 107/76 159/229 588/1240 107/297][Risk: ** Self-signed Cert **** TLS (probably) Not Carrying HTTPS **** Missing SNI TLS Extn **][Risk Score: 160][Risk Info: No ALPN / CN=hangouts][DTLSv1.2][JA3C: c14667d7da3e6f7a7ab5519ef78c2452][JA3S: 1f5d6a6d0bc5d514dd84d13e6283d309][Issuer: CN=hangouts][Subject: CN=hangouts][Certificate SHA-1: 6C:D0:9A:70:A1:F1:9E:BF:8E:EF:FE:B6:F1:37:A3:E8:8A:3B:F7:C8][Validity: 2022-03-17 02:11:17 - 2023-03-18 02:11:17][Cipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256][PLAIN TEXT (BwlkYDtFJ)][Plen Bins: 0,6,57,21,6,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0]
3 UDP [3516:bf0b:fc53:75e7:70af:f67f:8e49:f603]:56880 <-> [2a38:e156:8167:a333:face:b00c::24d9]:3478 [proto: 78/STUN][IP: 0/Unknown][ClearText][Confidence: DPI][DPI packets: 11][cat: Network/14][21 pkts/1722 bytes <-> 21 pkts/2226 bytes][Goodput ratio: 24/41][191.49 sec][bytes ratio: -0.128 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 2/2 9451/9451 10358/10358 2441/2441][Pkt Len c2s/s2c min/avg/max/stddev: 82/106 82/106 82/106 0/0][PLAIN TEXT (WOBTrOXR)][Plen Bins: 50,50,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]
4 TCP 87.47.100.17:3478 <-> 54.1.57.155:37257 [proto: 78/STUN][IP: 0/Unknown][ClearText][Confidence: DPI][DPI packets: 4][cat: Network/14][9 pkts/1494 bytes <-> 11 pkts/2178 bytes][Goodput ratio: 60/67][0.95 sec][Hostname/SNI: apps-host.com][bytes ratio: -0.186 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 104/96 267/252 102/93][Pkt Len c2s/s2c min/avg/max/stddev: 74/94 166/198 234/354 41/65][PLAIN TEXT (Unauthorized)][Plen Bins: 10,0,15,21,42,5,0,0,0,5,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]
5 TCP 10.77.110.51:41588 <-> 10.206.50.239:42000 [VLAN: 1611][proto: 78.38/STUN.Skype_TeamsCall][IP: 0/Unknown][ClearText][Confidence: DPI][DPI packets: 3][cat: VoIP/10][7 pkts/1006 bytes <-> 8 pkts/1118 bytes][Goodput ratio: 58/57][1.05 sec][bytes ratio: -0.053 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 189/134 369/399 144/153][Pkt Len c2s/s2c min/avg/max/stddev: 70/64 144/140 164/172 31/43][Plen Bins: 0,0,25,75,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]