diff options
author | Ivan Nardi <12729895+IvanNardi@users.noreply.github.com> | 2021-03-06 05:48:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-06 05:48:36 +0100 |
commit | c50a8d4808bbe3f41cbe5e681e84a1eb52129cb1 (patch) | |
tree | 804fe8ad8b7a16437fff526095d952d027f017c2 | |
parent | 73e434857037898ca13c763dcb6d0deb18150042 (diff) |
Add support for Snapchat voip calls (#1147)
* Add support for Snapchat voip calls
Snapchat multiplexes some of its audio/video real time traffic with QUIC
sessions. The peculiarity of these sessions is that they are Q046 and
don't have any SNI.
* Fix tests with libgcrypt disabled
-rw-r--r-- | example/ndpiReader.c | 2 | ||||
-rw-r--r-- | example/reader_util.c | 2 | ||||
-rw-r--r-- | src/include/ndpi_protocol_ids.h | 1 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 9 | ||||
-rw-r--r-- | src/lib/protocols/quic.c | 68 | ||||
-rwxr-xr-x | tests/do.sh.in | 2 | ||||
-rw-r--r-- | tests/pcap/snapchat_call.pcapng | bin | 0 -> 14856 bytes | |||
-rw-r--r-- | tests/result/snapchat_call.pcapng.out | 3 |
8 files changed, 84 insertions, 3 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c index 4df335d1c..0baa51000 100644 --- a/example/ndpiReader.c +++ b/example/ndpiReader.c @@ -81,7 +81,7 @@ 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 max_num_udp_dissected_pkts = 16 /* 8 is enough for most protocols, Signal requires more */, max_num_tcp_dissected_pkts = 80 /* due to telnet */; +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; static u_int16_t num_loops = 1; diff --git a/example/reader_util.c b/example/reader_util.c index e2a691576..257b5658f 100644 --- a/example/reader_util.c +++ b/example/reader_util.c @@ -85,7 +85,7 @@ extern u_int8_t enable_protocol_guess, enable_joy_stats, enable_payload_analyzer; extern u_int8_t verbose, human_readeable_string_len; -extern u_int8_t max_num_udp_dissected_pkts /* 8 */, max_num_tcp_dissected_pkts /* 10 */; +extern u_int8_t max_num_udp_dissected_pkts /* 24 */, max_num_tcp_dissected_pkts /* 80 */; static u_int32_t flow_id = 0; u_int8_t enable_doh_dot_detection = 0; diff --git a/src/include/ndpi_protocol_ids.h b/src/include/ndpi_protocol_ids.h index 58467770f..cbcde77af 100644 --- a/src/include/ndpi_protocol_ids.h +++ b/src/include/ndpi_protocol_ids.h @@ -283,6 +283,7 @@ typedef enum { NDPI_PROTOCOL_ANYDESK = 252, /* Toni Uhlig <matzeton@googlemail.com> */ NDPI_PROTOCOL_SOAP = 253, /* Toni Uhlig <matzeton@googlemail.com> */ NDPI_PROTOCOL_APPLE_SIRI = 254, /* Zied Aouini <aouinizied@gmail.com> */ + NDPI_PROTOCOL_SNAPCHAT_CALL = 255, #ifdef CUSTOM_NDPI_PROTOCOLS diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index daf02f76a..a9c3c9665 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -1576,6 +1576,10 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp 1 /* can_have_a_subprotocol */, no_master, no_master, "AppleSiri", NDPI_PROTOCOL_CATEGORY_VIRTUAL_ASSISTANT, ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */, ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */); + ndpi_set_proto_defaults(ndpi_str, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_SNAPCHAT_CALL, + 0 /* can_have_a_subprotocol */, no_master, no_master, "SnapchatCall", + NDPI_PROTOCOL_CATEGORY_VOIP, ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */, + ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */); #ifdef CUSTOM_NDPI_PROTOCOLS #include "../../../nDPI-custom/custom_ndpi_main.c" @@ -7069,6 +7073,11 @@ uint8_t ndpi_connection_tracking(struct ndpi_detection_module_struct *ndpi_str, if(flow->extra_packets_func) return(1); break; + + case NDPI_PROTOCOL_QUIC: + if(flow->extra_packets_func) + return(1); + break; } return(0); diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c index dd3493064..437968df4 100644 --- a/src/lib/protocols/quic.c +++ b/src/lib/protocols/quic.c @@ -1382,6 +1382,65 @@ static int may_be_initial_pkt(struct ndpi_detection_module_struct *ndpi_struct, /* ***************************************************************** */ +static int eval_extra_processing(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, u_int32_t version) +{ + /* For the time being we need extra processing only to detect Snapchat calls, + i.e. RTP/RTCP multiplxed with QUIC. + We noticed that Snapchat uses Q046, without any SNI */ + + if(version == V_Q046 && + flow->protos.tls_quic_stun.tls_quic.client_requested_server_name[0] == '\0') { + NDPI_LOG_DBG2(ndpi_struct, "We have further work to do\n"); + return 1; + } + return 0; +} + +static int is_valid_rtp_payload_type(uint8_t type) +{ + /* https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ + return type <= 34 || (type >= 96 && type <= 127); +} + +static int ndpi_search_quic_extra(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) +{ + struct ndpi_packet_struct *packet = &flow->packet; + + /* We are elaborating a packet following the initial CHLO/ClientHello. + Mutiplexing QUIC with RTP/RTCP should be quite generic, but + for the time being, we known only NDPI_PROTOCOL_SNAPCHAT_CALL having such behaviour */ + + NDPI_LOG_DBG(ndpi_struct, "search QUIC extra func\n"); + + /* If this packet is still a Q046 one we need to keep going */ + if(packet->payload[0] & 0x40) { + NDPI_LOG_DBG(ndpi_struct, "Still QUIC\n"); + return 1; /* Keep going */ + } + + NDPI_LOG_DBG2(ndpi_struct, "No more QUIC: nothing to do on QUIC side\n"); + flow->extra_packets_func = NULL; + + /* This might be a RTP/RTCP stream: let's check it */ + /* TODO: the cleanest solution should be triggering the rtp/rtcp dissector, but + I have not been able to that that so I reimplemented a basic RTP/RTCP detection.*/ + if((packet->payload[0] >> 6) == 2 && /* Version 2 */ + packet->payload_packet_len > 1 && + (packet->payload[1] == 201 || /* RTCP, Receiver Report */ + packet->payload[1] == 200 || /* RTCP, Sender Report */ + is_valid_rtp_payload_type(packet->payload[1] & 0x7F)) /* RTP */) { + NDPI_LOG_DBG(ndpi_struct, "Found RTP/RTCP over QUIC\n"); + ndpi_int_change_protocol(ndpi_struct, flow, NDPI_PROTOCOL_SNAPCHAT_CALL, NDPI_PROTOCOL_QUIC); + } else { + /* Unexpected traffic pattern: we should investigate it... */ + NDPI_LOG_INFO(ndpi_struct, "To investigate...\n"); + } + + return 0; +} + void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { @@ -1465,6 +1524,15 @@ void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, if(is_version_with_encrypted_header(version)) { ndpi_free(clear_payload); } + + /* + * 7) We need to process other packets than ClientHello/CHLO? + */ + if(eval_extra_processing(ndpi_struct, flow, version)) { + flow->check_extra_packets = 1; + flow->max_extra_packets_to_check = 24; /* TODO */ + flow->extra_packets_func = ndpi_search_quic_extra; + } } /* ***************************************************************** */ diff --git a/tests/do.sh.in b/tests/do.sh.in index 193698d58..85ef1ff63 100755 --- a/tests/do.sh.in +++ b/tests/do.sh.in @@ -5,7 +5,7 @@ cd "$(dirname "${0}")" FUZZY_TESTING_ENABLED=@BUILD_FUZZTARGETS@ GCRYPT_ENABLED=@GCRYPT_ENABLED@ -GCRYPT_PCAPS="gquic.pcap quic-23.pcap quic-24.pcap quic-27.pcap quic-28.pcap quic-29.pcap quic-mvfst-22.pcap quic-mvfst-27.pcap quic-mvfst-exp.pcap quic_q50.pcap quic_t50.pcap quic_t51.pcap quic_0RTT.pcap quic_interop_V.pcapng quic-33.pcapng doq.pcapng doq_adguard.pcapng dlt_ppp.pcap os_detected.pcapng" +GCRYPT_PCAPS="gquic.pcap quic-23.pcap quic-24.pcap quic-27.pcap quic-28.pcap quic-29.pcap quic-mvfst-22.pcap quic-mvfst-27.pcapng quic-mvfst-exp.pcap quic_q50.pcap quic_t50.pcap quic_t51.pcap quic_0RTT.pcap quic_interop_V.pcapng quic-33.pcapng doq.pcapng doq_adguard.pcapng dlt_ppp.pcap os_detected.pcapng" READER="../example/ndpiReader -p ../example/protos.txt -c ../example/categories.txt -r ../example/risky_domains.txt -j ../example/ja3_fingerprints.csv -S ../example/sha1_fingerprints.csv" RC=0 diff --git a/tests/pcap/snapchat_call.pcapng b/tests/pcap/snapchat_call.pcapng Binary files differnew file mode 100644 index 000000000..cdcac9894 --- /dev/null +++ b/tests/pcap/snapchat_call.pcapng diff --git a/tests/result/snapchat_call.pcapng.out b/tests/result/snapchat_call.pcapng.out new file mode 100644 index 000000000..ee151591b --- /dev/null +++ b/tests/result/snapchat_call.pcapng.out @@ -0,0 +1,3 @@ +SnapchatCall 50 12772 1 + + 1 UDP 192.168.12.169:42083 <-> 18.184.138.142:443 [proto: 188.255/QUIC.SnapchatCall][cat: Web/5][25 pkts/5295 bytes <-> 25 pkts/7477 bytes][Goodput ratio: 80/86][8.29 sec][bytes ratio: -0.171 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 288/246 1313/1315 376/342][Pkt Len c2s/s2c min/avg/max/stddev: 65/62 212/299 1392/1392 365/419][Risk: ** SNI TLS extension was missing **][PLAIN TEXT (AESGCC20)][Plen Bins: 28,44,0,2,2,0,0,2,4,4,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0] |