Skip to content

Commit

Permalink
Added NDPI_MALWARE_HOST_CONTACTED flow risk
Browse files Browse the repository at this point in the history
  • Loading branch information
lucaderi committed Oct 12, 2023
1 parent 30fc89e commit b617946
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 45 deletions.
6 changes: 6 additions & 0 deletions doc/flow_risks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions src/include/ndpi_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/include/ndpi_typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
100 changes: 58 additions & 42 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 &&
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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))
) {
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/lib/ndpi_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions tests/cfgs/default/result/malware.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -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]
1 change: 1 addition & 0 deletions wireshark/ndpi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit b617946

Please sign in to comment.