diff options
author | Ivan Nardi <12729895+IvanNardi@users.noreply.github.com> | 2024-09-27 18:51:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-27 18:51:47 +0200 |
commit | e2ed23a72ae6027a52f7d92a0e96c56af8459600 (patch) | |
tree | 9acb189766f25f7a7e161459ba7b87005f295b5f | |
parent | 9c35627d874c4f6aca50abce037b55fc279fab68 (diff) |
Let the library returning the packet direction calculated internally (#2572)
wireshark, lua: add basic analysis of possible obfuscated flows
-rw-r--r-- | example/ndpiReader.c | 13 | ||||
-rw-r--r-- | example/reader_util.c | 6 | ||||
-rw-r--r-- | example/reader_util.h | 2 | ||||
-rw-r--r-- | python/ndpi/ndpi_build.py | 2 | ||||
-rw-r--r-- | src/include/ndpi_api.h | 4 | ||||
-rw-r--r-- | src/include/ndpi_private.h | 2 | ||||
-rw-r--r-- | src/include/ndpi_typedefs.h | 2 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 20 | ||||
-rw-r--r-- | wireshark/ndpi.lua | 89 |
9 files changed, 122 insertions, 18 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c index 32b7399cd..6fe6e3e00 100644 --- a/example/ndpiReader.c +++ b/example/ndpiReader.c @@ -236,6 +236,7 @@ struct ndpi_packet_trailer { u_int32_t magic; /* WIRESHARK_NTOP_MAGIC */ ndpi_master_app_protocol proto; char name[16]; + u_int8_t flags; ndpi_risk flow_risk; u_int16_t flow_score; u_int16_t flow_risk_info_len; @@ -248,7 +249,7 @@ struct ndpi_packet_trailer { static pcap_dumper_t *extcap_dumper = NULL; static pcap_t *extcap_fifo_h = NULL; -static char extcap_buf[16384]; +static char extcap_buf[65536 + sizeof(struct ndpi_packet_trailer)]; static char *extcap_capture_fifo = NULL; static u_int16_t extcap_packet_filter = (u_int16_t)-1; static int do_extcap_capture = 0; @@ -4565,13 +4566,19 @@ static void ndpi_process_packet(u_char *args, memcpy(extcap_buf, packet, h.caplen); memset(trailer, 0, sizeof(struct ndpi_packet_trailer)); trailer->magic = htonl(WIRESHARK_NTOP_MAGIC); + if(flow) { + trailer->flags = flow->current_pkt_from_client_to_server; + trailer->flags |= (flow->detection_completed << 2); + } else { + trailer->flags = 0 | (2 << 2); + } trailer->flow_risk = htonl64(flow_risk); trailer->flow_score = htons(ndpi_risk2score(flow_risk, &cli_score, &srv_score)); trailer->flow_risk_info_len = ntohs(WIRESHARK_FLOW_RISK_INFO_SIZE); - if(flow->risk_str) { + if(flow && flow->risk_str) { strncpy(trailer->flow_risk_info, flow->risk_str, sizeof(trailer->flow_risk_info)); - trailer->flow_risk_info[sizeof(trailer->flow_risk_info) - 1] = '\0'; } + trailer->flow_risk_info[sizeof(trailer->flow_risk_info) - 1] = '\0'; trailer->proto.master_protocol = htons(p.proto.master_protocol), trailer->proto.app_protocol = htons(p.proto.app_protocol); ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, p, trailer->name, sizeof(trailer->name)); diff --git a/example/reader_util.c b/example/reader_util.c index 287f133cd..c0c723da0 100644 --- a/example/reader_util.c +++ b/example/reader_util.c @@ -1733,7 +1733,6 @@ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow, flow->detected_protocol = ndpi_detection_process_packet(workflow->ndpi_struct, ndpi_flow, iph ? (uint8_t *)iph : (uint8_t *)iph6, ipsize, time_ms, &input_info); - enough_packets |= ndpi_flow->fail_with_unknown; if(enough_packets || (flow->detected_protocol.proto.app_protocol != NDPI_PROTOCOL_UNKNOWN)) { if((!enough_packets) @@ -1754,7 +1753,12 @@ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow, process_ndpi_collected_info(workflow, flow); } } + /* Let's try to save client-server direction */ + flow->current_pkt_from_client_to_server = input_info.in_pkt_dir; + malloc_size_stats = 0; + } else { + flow->current_pkt_from_client_to_server = NDPI_IN_PKT_DIR_UNKNOWN; /* Unknown */ } #if 0 diff --git a/example/reader_util.h b/example/reader_util.h index 27b8e1c0b..ef15053cd 100644 --- a/example/reader_util.h +++ b/example/reader_util.h @@ -182,7 +182,7 @@ typedef struct ndpi_flow_info { struct ndpi_in6_addr dst_ip6; /* network order */ u_int16_t src_port; /* network order */ u_int16_t dst_port; /* network order */ - u_int8_t detection_completed, protocol, bidirectional, check_extra_packets; + u_int8_t detection_completed, protocol, bidirectional, check_extra_packets, current_pkt_from_client_to_server; u_int16_t vlan_id; ndpi_packet_tunnel tunnel_type; struct ndpi_flow_struct *ndpi_flow; diff --git a/python/ndpi/ndpi_build.py b/python/ndpi/ndpi_build.py index 07aac0de3..e2eac98e4 100644 --- a/python/ndpi/ndpi_build.py +++ b/python/ndpi/ndpi_build.py @@ -57,7 +57,7 @@ ndpi_protocol ndpi_detection_process_packet(struct ndpi_detection_module_struct const unsigned char *packet, const unsigned short packetlen, const u_int64_t packet_time_ms, - const struct ndpi_flow_input_info *input_info); + struct ndpi_flow_input_info *input_info); ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow, u_int8_t *protocol_was_guessed); diff --git a/src/include/ndpi_api.h b/src/include/ndpi_api.h index d2ba9816e..edfb497d4 100644 --- a/src/include/ndpi_api.h +++ b/src/include/ndpi_api.h @@ -334,7 +334,7 @@ extern "C" { const unsigned char *packet, const unsigned short packetlen, const u_int64_t packet_time_ms, - const struct ndpi_flow_input_info *input_info); + struct ndpi_flow_input_info *input_info); /** * Processes one packet and returns the ID of the detected protocol. @@ -354,7 +354,7 @@ extern "C" { const unsigned char *packet, const unsigned short packetlen, const u_int64_t packet_time_ms, - const struct ndpi_flow_input_info *input_info); + struct ndpi_flow_input_info *input_info); /** * Get the main protocol of the passed flows for the detected module * diff --git a/src/include/ndpi_private.h b/src/include/ndpi_private.h index 3aa17ed3c..c7eef1e0b 100644 --- a/src/include/ndpi_private.h +++ b/src/include/ndpi_private.h @@ -404,7 +404,7 @@ struct ndpi_detection_module_struct { /* Current packet */ struct ndpi_packet_struct packet; - const struct ndpi_flow_input_info *input_info; + struct ndpi_flow_input_info *input_info; #ifdef HAVE_NBPF u_int8_t num_nbpf_custom_proto; diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index fc0a2cf16..1973bd981 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -645,7 +645,7 @@ struct ndpi_gre_basehdr { * Optional information about flow management (per packet) */ struct ndpi_flow_input_info { - unsigned char in_pkt_dir; + unsigned char in_pkt_dir; /* If unknown, the library might *returns* to the application the direction calculated internally */ unsigned char seen_flow_beginning; }; diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index 3e16ca5c1..ef5bab840 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -6804,7 +6804,7 @@ static int ndpi_init_packet(struct ndpi_detection_module_struct *ndpi_str, const u_int64_t current_time_ms, const unsigned char *packet_data, unsigned short packetlen, - const struct ndpi_flow_input_info *input_info) { + struct ndpi_flow_input_info *input_info) { struct ndpi_packet_struct *packet = &ndpi_str->packet; const struct ndpi_iphdr *decaps_iph = NULL; u_int16_t l3len; @@ -7261,6 +7261,14 @@ static void ndpi_connection_tracking(struct ndpi_detection_module_struct *ndpi_s ndpi_unset_risk(flow, NDPI_UNIDIRECTIONAL_TRAFFIC); /* Clear bit */ } } + + if(ndpi_str->input_info && + ndpi_str->input_info->in_pkt_dir == NDPI_IN_PKT_DIR_UNKNOWN) { + if(current_pkt_from_client_to_server(ndpi_str, flow)) + ndpi_str->input_info->in_pkt_dir = NDPI_IN_PKT_DIR_C_TO_S; + else + ndpi_str->input_info->in_pkt_dir = NDPI_IN_PKT_DIR_S_TO_C; + } } /* ************************************************ */ @@ -7959,7 +7967,7 @@ 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) { + struct ndpi_flow_input_info *input_info) { if(flow == NULL) return; @@ -8562,7 +8570,7 @@ static ndpi_protocol ndpi_internal_detection_process_packet(struct ndpi_detectio 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_flow_input_info *input_info) { struct ndpi_packet_struct *packet; NDPI_SELECTION_BITMASK_PROTOCOL_SIZE ndpi_selection_packet; u_int32_t num_calls = 0; @@ -8593,6 +8601,10 @@ static ndpi_protocol ndpi_internal_detection_process_packet(struct ndpi_detectio if(ndpi_str->cfg.max_packets_to_process > 0 && flow->num_processed_pkts >= ndpi_str->cfg.max_packets_to_process) { flow->extra_packets_func = NULL; /* To allow ndpi_extra_dissection_possible() to fail */ flow->fail_with_unknown = 1; + /* Let's try to update ndpi_str->input_info->in_pkt_dir even in this case. + * It is quite uncommon, so we are not going to spend a lot of resources here... */ + if(ndpi_init_packet(ndpi_str, flow, current_time_ms, packet_data, packetlen, input_info) == 0) + ndpi_connection_tracking(ndpi_str, flow); return(ret); /* Avoid spending too much time with this flow */ } @@ -8892,7 +8904,7 @@ static ndpi_protocol ndpi_internal_detection_process_packet(struct ndpi_detectio ndpi_protocol ndpi_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, - const struct ndpi_flow_input_info *input_info) { + 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/wireshark/ndpi.lua b/wireshark/ndpi.lua index adc0fd786..cfd41ab05 100644 --- a/wireshark/ndpi.lua +++ b/wireshark/ndpi.lua @@ -35,6 +35,19 @@ ndpi_fds.magic = ProtoField.new("nDPI Magic", "ndpi.magic", ftype ndpi_fds.network_protocol = ProtoField.new("nDPI Network Protocol", "ndpi.protocol.network", ftypes.UINT8, nil, base.DEC) ndpi_fds.application_protocol = ProtoField.new("nDPI Application Protocol", "ndpi.protocol.application", ftypes.UINT8, nil, base.DEC) ndpi_fds.name = ProtoField.new("nDPI Protocol Name", "ndpi.protocol.name", ftypes.STRING) +ndpi_fds.flags = ProtoField.new("nDPI Flags", "ndpi.flags", ftypes.UINT8, nil, base.HEX) +local dir_types = { + [0] = "Unknown Direction", + [1] = "Client to Server Direction", + [2] = "Server to Client Direction", +} +ndpi_fds.flags_direction = ProtoField.new("nDPI Direction", "ndpi.flags.direction", ftypes.UINT8, dir_types, base.DEC, 0x03) +local dpi_state_types = { + [0] = "Inspecting", + [1] = "From Inspecting to Done", + [2] = "Done", +} +ndpi_fds.flags_dpi_state = ProtoField.new("nDPI DPI state", "ndpi.flags.dpi_state", ftypes.UINT8, dpi_state_types, base.DEC, 0xC) ndpi_fds.flow_risk = ProtoField.new("nDPI Flow Risk", "ndpi.flow_risk", ftypes.UINT64, nil, base.HEX) ndpi_fds.flow_score = ProtoField.new("nDPI Flow Score", "ndpi.flow_score", ftypes.UINT32) ndpi_fds.flow_risk_info_len = ProtoField.new("nDPI Flow Risk Info Length", "ndpi.flow_risk_info_len", ftypes.UINT16, nil, base.DEC) @@ -54,8 +67,6 @@ ndpi_fds.metadata_length = ProtoField.new("nDPI Metadata Length", "ndpi.met ndpi_fds.metadata_value = ProtoField.new("nDPI Metadata Value", "ndpi.metadata.value", ftypes.BYTES) -- Specific fields ndpi_fds.metadata_server_name = ProtoField.new("nDPI Server Name", "ndpi.metadata.server_name", ftypes.STRING) -ndpi_fds.metadata_ja3c = ProtoField.new("nDPI JA3C", "ndpi.metadata.ja3c", ftypes.STRING) -ndpi_fds.metadata_ja3s = ProtoField.new("nDPI JA3S", "ndpi.metadata.ja3s", ftypes.STRING) ndpi_fds.metadata_ja4c = ProtoField.new("nDPI JA4C", "ndpi.metadata.ja4c", ftypes.STRING) @@ -193,6 +204,9 @@ local tot_tls_flows = 0 local http_ua = {} local tot_http_ua_flows = 0 +local possible_obfuscated_servers = {} +local tot_obfuscated_flows = 0 + local flows = {} local tot_flows = 0 @@ -220,6 +234,8 @@ local debug = false local dump_timeseries = false +local track_obfuscated_servers = true + local dissect_ndpi_trailer = true local dump_file = "/tmp/wireshark-influx.txt" @@ -440,6 +456,10 @@ function ndpi_proto.init() http_ua = {} tot_http_ua_flows = 0 + -- Obfuscated servers + possible_obfuscated_servers = {} + tot_obfuscated_flows = 0 + -- Flows flows = {} tot_flows = 0 @@ -1111,6 +1131,13 @@ function ndpi_proto.dissector(tvb, pinfo, tree) --print(network_protocol .. "/" .. application_protocol .. "/".. name) end + ndpi_subtree:add(ndpi_fds.flags, trailer_tvb(offset, 1)) + ndpi_subtree:add(ndpi_fds.flags_direction, trailer_tvb(offset, 1)) + local direction = trailer_tvb(offset, 1):bitfield(6,2) -- From left to right!! -> inverted values + ndpi_subtree:add(ndpi_fds.flags_dpi_state, trailer_tvb(offset, 1)) + local dpi_state = trailer_tvb(offset, 1):bitfield(4,2) -- From left to right!! -> inverted values + offset = offset + 1 + flow_risk_tree = ndpi_subtree:add(ndpi_fds.flow_risk, trailer_tvb(offset, 8)) flow_risk = trailer_tvb(offset, 8):uint64() -- UInt64 object! offset = offset + 8 @@ -1133,11 +1160,15 @@ function ndpi_proto.dissector(tvb, pinfo, tree) for i=0,63 do if flow_risks[i] ~= nil then - flow_risk_tree:add(flow_risks[i], trailer_tvb(24, 8)) + flow_risk_tree:add(flow_risks[i], trailer_tvb(25, 8)) end end - flow_risk_tree:add(flow_risks[64], trailer_tvb(24, 8)) -- Unused bits in flow risk bitmask + flow_risk_tree:add(flow_risks[64], trailer_tvb(25, 8)) -- Unused bits in flow risk bitmask + + flow_risk_obfuscated_traffic = trailer_tvb(25, 8):bitfield(7,1) -- From left to right + else + flow_risk_obfuscated_traffic = 0 end if(flow_score > 0) then @@ -1226,6 +1257,29 @@ function ndpi_proto.dissector(tvb, pinfo, tree) ndpi_flows[flowkey] = ndpi_flows[flowkey] + pinfo.len end + + if(track_obfuscated_servers and pinfo.visited == false) then + -- Only once per flow, when DPI ends + if(dpi_state == 1) then + if(direction == 2) then -- current packet from server to client + key = tostring(pinfo.src) .. ":" .. getstring(pinfo.src_port) .. " " .. name + else + key = tostring(pinfo.dst) .. ":" .. getstring(pinfo.dst_port) .. " " .. name + end + if(possible_obfuscated_servers[key] == nil) then + possible_obfuscated_servers[key] = {1, flow_risk_obfuscated_traffic} + else + possible_obfuscated_servers[key][1] = possible_obfuscated_servers[key][1] + 1 + if(flow_risk_obfuscated_traffic == 1) then + possible_obfuscated_servers[key][2] = possible_obfuscated_servers[key][2] + 1 + end + end + + if(flow_risk_obfuscated_traffic == 1) then + tot_obfuscated_flows = tot_obfuscated_flows + 1 + end + end + end end end -- nDPI @@ -1569,6 +1623,32 @@ end -- ############################################### +local function obfuscated_servers_dialog_menu() + local win = TextWindow.new("Obfuscated Servers Analysis"); + local label = "" + local tot = 0 + local i + + if(tot_obfuscated_flows > 0) then + i = 0 + label = label .. "Server\t\tProtocol\tTotal Flows\tObfuscated flows\n" + for k,v in pairsByKeys(possible_obfuscated_servers, rev) do + for token in string.gmatch(k, "[^%s]+") do -- split key in two token (for beter formatting): ip:port and protocol + label = label .. token .. "\t" + end + label = label .. v[1] .. "\t\t" .. v[2] .. "\n" + end + label = label .. "\n\nTotal obfuscated flows: " .. tot_obfuscated_flows .. "\n" + else + label = "No possible Obfuscated Servers detected" + end + + win:set(label) + win:add_button("Clear", function() win:clear() end) +end + +-- ############################################### + local function http_ua_dialog_menu() local win = TextWindow.new("HTTP User Agent"); local label = "" @@ -1766,6 +1846,7 @@ register_menu("ntop/TCP Analysis", tcp_dialog_menu, MENU_TOOLS_UNSORTED) register_menu("ntop/VLAN", vlan_dialog_menu, MENU_TOOLS_UNSORTED) register_menu("ntop/Latency/Network", rtt_dialog_menu, MENU_TOOLS_UNSORTED) register_menu("ntop/Latency/Application", appl_rtt_dialog_menu, MENU_TOOLS_UNSORTED) +register_menu("ntop/Obfuscated Servers Analysis", obfuscated_servers_dialog_menu, MENU_TOOLS_UNSORTED) -- ############################################### |