diff options
-rw-r--r-- | doc/flow_risks.rst | 6 | ||||
-rw-r--r-- | src/include/ndpi_api.h | 1 | ||||
-rw-r--r-- | src/include/ndpi_typedefs.h | 3 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 100 | ||||
-rw-r--r-- | src/lib/ndpi_utils.c | 4 | ||||
-rw-r--r-- | tests/cfgs/default/result/malware.pcap.out | 4 | ||||
-rw-r--r-- | wireshark/ndpi.lua | 1 |
7 files changed, 74 insertions, 45 deletions
diff --git a/doc/flow_risks.rst b/doc/flow_risks.rst index 7cecf5f42..08ded148a 100644 --- a/doc/flow_risks.rst +++ b/doc/flow_risks.rst @@ -322,3 +322,9 @@ Flow with Unknown protocol containing encrypted traffic. NDPI_TLS_ALPN_SNI_MISMATCH ========================= Invalid TLS ALPN/SNI mismatch. For instance ALPN advertises the flow as h2 (HTTP/2.0) and no SNI is reported. + +.. _Risk 053 + +NDPI_MALWARE_CONTACTED +====================== +Client contacted a server host labelled as malware. diff --git a/src/include/ndpi_api.h b/src/include/ndpi_api.h index 51c56c8f3..13e11f651 100644 --- a/src/include/ndpi_api.h +++ b/src/include/ndpi_api.h @@ -1026,6 +1026,7 @@ extern "C" { void* ndpi_find_ipv4_category_userdata(struct ndpi_detection_module_struct *ndpi_str, u_int32_t saddr); int ndpi_fill_ip_protocol_category(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, u_int32_t saddr, u_int32_t daddr, ndpi_protocol *ret); diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 5000b0cb9..555a876ec 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -159,7 +159,8 @@ typedef enum { NDPI_TCP_ISSUES, /* 50 */ /* TCP issues such as connection failed, probing or scan */ NDPI_FULLY_ENCRYPTED, /* This (unknown) session is fully encrypted */ NDPI_TLS_ALPN_SNI_MISMATCH, /* Invalid ALPN/SNI combination */ - + NDPI_MALWARE_HOST_CONTACTED, /* Flow client contacted a malware host */ + /* Leave this as last member */ NDPI_MAX_RISK /* must be <= 63 due to (**) */ } ndpi_risk_enum; diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index 87d43f3f2..4964a0e1d 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -191,10 +191,12 @@ static ndpi_risk_info ndpi_known_risks[] = { { NDPI_TCP_ISSUES, NDPI_RISK_MEDIUM, CLIENT_FAIR_RISK_PERCENTAGE, NDPI_CLIENT_ACCOUNTABLE }, { NDPI_FULLY_ENCRYPTED, NDPI_RISK_MEDIUM, CLIENT_FAIR_RISK_PERCENTAGE, NDPI_CLIENT_ACCOUNTABLE }, { NDPI_TLS_ALPN_SNI_MISMATCH, NDPI_RISK_MEDIUM, CLIENT_FAIR_RISK_PERCENTAGE, NDPI_CLIENT_ACCOUNTABLE }, + { NDPI_MALWARE_HOST_CONTACTED, NDPI_RISK_SEVERE, CLIENT_HIGH_RISK_PERCENTAGE, NDPI_CLIENT_ACCOUNTABLE }, /* Leave this as last member */ { NDPI_MAX_RISK, NDPI_RISK_LOW, CLIENT_FAIR_RISK_PERCENTAGE, NDPI_NO_ACCOUNTABILITY } }; + #if !defined(NDPI_CFFI_PREPROCESSING) && defined(__linux__) #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L _Static_assert(sizeof(ndpi_known_risks) / sizeof(ndpi_risk_info) == NDPI_MAX_RISK + 1, @@ -6836,7 +6838,8 @@ ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_st /* ********************************************************************************* */ -void ndpi_process_extra_packet(struct ndpi_detection_module_struct *ndpi_str, struct ndpi_flow_struct *flow, +void ndpi_process_extra_packet(struct ndpi_detection_module_struct *ndpi_str, + struct ndpi_flow_struct *flow, const unsigned char *packet_data, const unsigned short packetlen, const u_int64_t current_time_ms, const struct ndpi_flow_input_info *input_info) { @@ -7018,9 +7021,11 @@ void* ndpi_find_ipv4_category_userdata(struct ndpi_detection_module_struct *ndpi /* NOTE u_int32_t is represented in network byte order */ int ndpi_fill_ip_protocol_category(struct ndpi_detection_module_struct *ndpi_str, + struct ndpi_flow_struct *flow, u_int32_t saddr, u_int32_t daddr, ndpi_protocol *ret) { - + bool match_client = true; + ret->custom_category_userdata = NULL; if(ndpi_str->custom_categories.categories_loaded && @@ -7038,17 +7043,25 @@ int ndpi_fill_ip_protocol_category(struct ndpi_detection_module_struct *ndpi_str node = ndpi_patricia_search_best(ndpi_str->custom_categories.ipAddresses, &prefix); } - if(!node) { + if(node == NULL) { if(daddr != 0) { ndpi_fill_prefix_v4(&prefix, (struct in_addr *) &daddr, 32, ((ndpi_patricia_tree_t *) ndpi_str->custom_categories.ipAddresses)->maxbits); node = ndpi_patricia_search_best(ndpi_str->custom_categories.ipAddresses, &prefix); + match_client = false; } + } else { + match_client = true; } if(node) { ret->category = (ndpi_protocol_category_t) node->value.u.uv32.user_value; ret->custom_category_userdata = node->custom_user_data; + + if((ret->category == CUSTOM_CATEGORY_MALWARE) && (match_client == false)) { + ndpi_set_risk(ndpi_str, flow, NDPI_MALWARE_HOST_CONTACTED, "Client contacted malware host"); + } + return(1); } } @@ -7160,7 +7173,7 @@ static int ndpi_do_guess(struct ndpi_detection_module_struct *ndpi_str, struct n if(ndpi_str->custom_categories.categories_loaded && packet->iph) { if(ndpi_str->ndpi_num_custom_protocols != 0) - ndpi_fill_ip_protocol_category(ndpi_str, flow->c_address.v4, flow->s_address.v4, ret); + ndpi_fill_ip_protocol_category(ndpi_str, flow, flow->c_address.v4, flow->s_address.v4, ret); flow->guessed_header_category = ret->category; } else flow->guessed_header_category = NDPI_PROTOCOL_CATEGORY_UNSPECIFIED; @@ -7204,8 +7217,10 @@ static int ndpi_do_guess(struct ndpi_detection_module_struct *ndpi_str, struct n /* ********************************************************************************* */ static ndpi_protocol ndpi_internal_detection_process_packet(struct ndpi_detection_module_struct *ndpi_str, - struct ndpi_flow_struct *flow, const unsigned char *packet_data, - const unsigned short packetlen, const u_int64_t current_time_ms, + struct ndpi_flow_struct *flow, + const unsigned char *packet_data, + const unsigned short packetlen, + const u_int64_t current_time_ms, const struct ndpi_flow_input_info *input_info) { struct ndpi_packet_struct *packet; NDPI_SELECTION_BITMASK_PROTOCOL_SIZE ndpi_selection_packet; @@ -7262,47 +7277,50 @@ static ndpi_protocol ndpi_internal_detection_process_packet(struct ndpi_detectio if(ndpi_init_packet(ndpi_str, flow, current_time_ms, packet_data, packetlen, input_info) != 0) return(ret); + if(flow->num_processed_pkts == 1) { + /* first packet of this flow to be analyzed */ + #ifdef HAVE_NBPF - if((flow->num_processed_pkts == 1) /* first packet of this flow to be analyzed */ - && (ndpi_str->nbpf_custom_proto[0].tree != NULL)) { - u_int8_t i; - nbpf_pkt_info_t t; - - memset(&t, 0, sizeof(t)); - - if(packet->iphv6 != NULL) { - t.tuple.eth_type = 0x86DD; - t.tuple.ip_version = 6; - memcpy(&t.tuple.ip_src.v6, &packet->iphv6->ip6_src, 16); - memcpy(&t.tuple.ip_dst.v6, &packet->iphv6->ip6_dst, 16); - } else { - t.tuple.eth_type = 0x0800; - t.tuple.ip_version = 4; - t.tuple.ip_src.v4 = packet->iph->saddr; - t.tuple.ip_dst.v4 = packet->iph->daddr; - } + if(ndpi_str->nbpf_custom_proto[0].tree != NULL) { + u_int8_t i; + nbpf_pkt_info_t t; + + memset(&t, 0, sizeof(t)); - t.tuple.l3_proto = flow->l4_proto; + if(packet->iphv6 != NULL) { + t.tuple.eth_type = 0x86DD; + t.tuple.ip_version = 6; + memcpy(&t.tuple.ip_src.v6, &packet->iphv6->ip6_src, 16); + memcpy(&t.tuple.ip_dst.v6, &packet->iphv6->ip6_dst, 16); + } else { + t.tuple.eth_type = 0x0800; + t.tuple.ip_version = 4; + t.tuple.ip_src.v4 = packet->iph->saddr; + t.tuple.ip_dst.v4 = packet->iph->daddr; + } + + t.tuple.l3_proto = flow->l4_proto; - if(packet->tcp) - t.tuple.l4_src_port = packet->tcp->source, t.tuple.l4_dst_port = packet->tcp->dest; - else if(packet->udp) - t.tuple.l4_src_port = packet->udp->source, t.tuple.l4_dst_port = packet->udp->dest; + if(packet->tcp) + t.tuple.l4_src_port = packet->tcp->source, t.tuple.l4_dst_port = packet->tcp->dest; + else if(packet->udp) + t.tuple.l4_src_port = packet->udp->source, t.tuple.l4_dst_port = packet->udp->dest; - for(i=0; (i<MAX_NBPF_CUSTOM_PROTO) && (ndpi_str->nbpf_custom_proto[i].tree != NULL); i++) { - if(nbpf_match(ndpi_str->nbpf_custom_proto[i].tree, &t)) { - /* match found */ - ret.master_protocol = ret.app_protocol = ndpi_str->nbpf_custom_proto[i].l7_protocol; - ndpi_fill_protocol_category(ndpi_str, flow, &ret); - ndpi_reconcile_protocols(ndpi_str, flow, &ret); - flow->confidence = NDPI_CONFIDENCE_NBPF; + for(i=0; (i<MAX_NBPF_CUSTOM_PROTO) && (ndpi_str->nbpf_custom_proto[i].tree != NULL); i++) { + if(nbpf_match(ndpi_str->nbpf_custom_proto[i].tree, &t)) { + /* match found */ + ret.master_protocol = ret.app_protocol = ndpi_str->nbpf_custom_proto[i].l7_protocol; + ndpi_fill_protocol_category(ndpi_str, flow, &ret); + ndpi_reconcile_protocols(ndpi_str, flow, &ret); + flow->confidence = NDPI_CONFIDENCE_NBPF; - return(ret); + return(ret); + } } } - } #endif - + } + ndpi_connection_tracking(ndpi_str, flow); /* build ndpi_selection packet bitmask */ @@ -7350,7 +7368,7 @@ static ndpi_protocol ndpi_internal_detection_process_packet(struct ndpi_detectio ndpi_fill_protocol_category(ndpi_str, flow, &ret); else ret.category = flow->category; - + if((!flow->risk_checked) && ((ret.master_protocol != NDPI_PROTOCOL_UNKNOWN) || (ret.app_protocol != NDPI_PROTOCOL_UNKNOWN)) ) { @@ -7505,8 +7523,6 @@ ndpi_protocol ndpi_detection_process_packet(struct ndpi_detection_module_struct struct ndpi_flow_struct *flow, const unsigned char *packet_data, const unsigned short packetlen, const u_int64_t current_time_ms, const struct ndpi_flow_input_info *input_info) { - - ndpi_protocol p = ndpi_internal_detection_process_packet(ndpi_str, flow, packet_data, packetlen, current_time_ms, input_info); diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index 73a1b5974..9e43c8607 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -2061,6 +2061,10 @@ const char* ndpi_risk2str(ndpi_risk_enum risk) { return("ALPN/SNI Mismatch"); break; + case NDPI_MALWARE_HOST_CONTACTED: + return("Client contacted a malware host"); + break; + default: ndpi_snprintf(buf, sizeof(buf), "%d", (int)risk); return(buf); diff --git a/tests/cfgs/default/result/malware.pcap.out b/tests/cfgs/default/result/malware.pcap.out index fffc361a5..8427d1db6 100644 --- a/tests/cfgs/default/result/malware.pcap.out +++ b/tests/cfgs/default/result/malware.pcap.out @@ -36,5 +36,5 @@ JA3 Host Stats: 1 TCP 192.168.7.7:35236 <-> 67.215.92.210:443 [proto: 91/TLS][IP: 225/OpenDNS][Encrypted][Confidence: DPI][DPI packets: 10][cat: Malware/100][11 pkts/1280 bytes <-> 9 pkts/5860 bytes][Goodput ratio: 53/91][0.64 sec][Hostname/SNI: www.internetbadguys.com][(Advertised) ALPNs: h2;http/1.1][TLS Supported Versions: TLSv1.3;TLSv1.2;TLSv1.1;TLSv1][bytes ratio: -0.641 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 71/75 240/249 99/103][Pkt Len c2s/s2c min/avg/max/stddev: 54/60 116/651 571/1514 148/644][Risk: ** TLS Cert Mismatch **][Risk Score: 100][Risk Info: www.internetbadguys.com vs api.opendns.com,branded-login.opendns.com,cachecheck.opendns.com,community.opendns.com,dashboard2.o][TLSv1.2][JA3C: b20b44b18b853ef29ab773e921b03422][ServerNames: api.opendns.com,branded-login.opendns.com,cachecheck.opendns.com,community.opendns.com,dashboard2.opendns.com,dashboard.opendns.com,dashboard-ipv4.opendns.com,msp-login.opendns.com,api-ipv4.opendns.com,api-ipv6.opendns.com,authz.api.opendns.com,domain.opendns.com,help.vpn.opendns.com,ideabank.opendns.com,login.opendns.com,netgear.opendns.com,reseller-login.opendns.com,images.opendns.com,images-using.opendns.com,store.opendns.com,signup.opendns.com,twilio.opendns.com,updates.opendns.com,shared.opendns.com,tools.opendns.com,cache.opendns.com,api.umbrella.com,branded-login.umbrella.com,cachecheck.umbrella.com,community.umbrella.com,dashboard2.umbrella.com,dashboard.umbrella.com,dashboard-ipv4.umbrella.com,msp-login.umbrella.com,api-ipv4.umbrella.com,api-ipv6.umbrella.com,authz.api.umbrella.com,domain.umbrella.com,help.vpn.umbrella.com,ideabank.umbrella.com,login.umbrella.com,netgear.umbrella.com,reseller-login.umbrella.com,images.umbrella.com,images-using.umbrella.com,store.umbrella.com,signup.umbrella.com,twilio.umbrella.com,updates.umbrella.com,shared.umbrella.com,tools.umbrella.com,cache.umbrella.com][JA3S: 0c0aff9ccea5e7e1de5c3a0069d103f3][Issuer: C=US, O=DigiCert Inc, CN=DigiCert SHA2 Secure Server CA][Subject: C=US, ST=California, L=San Francisco, O=OpenDNS, Inc., CN=api.opendns.com][Certificate SHA-1: 21:B4:CF:84:13:3A:21:A4:B0:02:63:76:39:84:EA:ED:27:EE:51:7C][Firefox][Validity: 2018-04-26 00:00:00 - 2020-07-29 00:00:00][Cipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256][Plen Bins: 12,0,0,12,0,0,0,0,12,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,0,0] 2 TCP 192.168.7.7:48394 <-> 67.215.92.210:80 [proto: 7/HTTP][IP: 225/OpenDNS][ClearText][Confidence: DPI][DPI packets: 2][cat: Malware/100][1 pkts/383 bytes <-> 1 pkts/98 bytes][Goodput ratio: 86/44][0.21 sec][Hostname/SNI: www.internetbadguys.com][URL: www.internetbadguys.com/][User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0][PLAIN TEXT (GET / HTTP/1.1)][Plen Bins: 0,50,0,0,0,0,0,0,0,0,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] 3 UDP 192.168.7.7:42370 <-> 1.1.1.1:53 [proto: 5/DNS][IP: 0/Unknown][ClearText][Confidence: DPI][DPI packets: 2][cat: Network/14][1 pkts/106 bytes <-> 1 pkts/110 bytes][Goodput ratio: 60/61][0.02 sec][Hostname/SNI: www.internetbadguys.com][67.215.92.210][PLAIN TEXT (internetbadguys)][Plen Bins: 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,0,0,0] - 4 ICMP 192.168.7.7:0 -> 144.139.247.220:0 [proto: 81/ICMP][IP: 0/Unknown][ClearText][Confidence: DPI][DPI packets: 1][cat: Malware/100][1 pkts/98 bytes -> 0 pkts/0 bytes][Goodput ratio: 57/0][< 1 sec][Risk: ** Unidirectional Traffic **][Risk Score: 10][Risk Info: No server to client traffic][Plen Bins: 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,0,0,0,0] - 5 TCP 192.168.7.7:33706 -> 144.139.247.220:80 [proto: 7/HTTP][IP: 0/Unknown][ClearText][Confidence: Match by port][DPI packets: 1][cat: Malware/100][1 pkts/66 bytes -> 0 pkts/0 bytes][Goodput ratio: 0/0][< 1 sec][Risk: ** Unidirectional Traffic **][Risk Score: 10][Risk Info: No server to client traffic][Plen Bins: 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,0] + 4 ICMP 192.168.7.7:0 -> 144.139.247.220:0 [proto: 81/ICMP][IP: 0/Unknown][ClearText][Confidence: DPI][DPI packets: 1][cat: Malware/100][1 pkts/98 bytes -> 0 pkts/0 bytes][Goodput ratio: 57/0][< 1 sec][Risk: ** Unidirectional Traffic **** Client contacted a malware host **][Risk Score: 160][Risk Info: No server to client traffic / Client contacted malware host][Plen Bins: 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,0,0,0,0] + 5 TCP 192.168.7.7:33706 -> 144.139.247.220:80 [proto: 7/HTTP][IP: 0/Unknown][ClearText][Confidence: Match by port][DPI packets: 1][cat: Malware/100][1 pkts/66 bytes -> 0 pkts/0 bytes][Goodput ratio: 0/0][< 1 sec][Risk: ** Unidirectional Traffic **** Client contacted a malware host **][Risk Score: 160][Risk Info: No server to client traffic / Client contacted malware host][Plen Bins: 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,0] diff --git a/wireshark/ndpi.lua b/wireshark/ndpi.lua index b3151b6e9..e3d3bdb53 100644 --- a/wireshark/ndpi.lua +++ b/wireshark/ndpi.lua @@ -91,6 +91,7 @@ flow_risks[49] = ProtoField.bool("ndpi.flow_risk.minor_issues", "Minor flow issu flow_risks[50] = ProtoField.bool("ndpi.flow_risk.tcp_issues", "TCP connection issues", num_bits_flow_risks, nil, bit(18), "nDPI Flow Risk: TCP connection issues") flow_risks[51] = ProtoField.bool("ndpi.flow_risk.fully_encrypted", "Fully encrypted connection", num_bits_flow_risks, nil, bit(19), "nDPI Flow Risk: Fully encrypted connection") flow_risks[52] = ProtoField.bool("ndpi.flow_risk.tls_alpn_sni_mismatch", "ALPN/SNI Mismatch", num_bits_flow_risks, nil, bit(20), "nDPI Flow Risk: ALPN/SNI Mismatch") +flow_risks[53] = ProtoField.bool("ndpi.flow_risk.malware_contact", "Contact with a malware host", num_bits_flow_risks, nil, bit(21), "nDPI Flow Risk: Malware host contacted") -- Last one: keep in sync the bitmask when adding new risks!! flow_risks[64] = ProtoField.new("Unused", "ndpi.flow_risk.unused", ftypes.UINT32, nil, base.HEX, bit(32) - bit(20)) |