aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/flow_risks.rst6
-rw-r--r--src/include/ndpi_api.h1
-rw-r--r--src/include/ndpi_typedefs.h3
-rw-r--r--src/lib/ndpi_main.c100
-rw-r--r--src/lib/ndpi_utils.c4
-rw-r--r--tests/cfgs/default/result/malware.pcap.out4
-rw-r--r--wireshark/ndpi.lua1
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))