Skip to content

Commit

Permalink
Let the library returning the packet direction calculated internally (#…
Browse files Browse the repository at this point in the history
…2572)

wireshark, lua: add basic analysis of possible obfuscated flows
  • Loading branch information
IvanNardi authored Sep 27, 2024
1 parent 9c35627 commit e2ed23a
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 18 deletions.
13 changes: 10 additions & 3 deletions example/ndpiReader.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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));

Expand Down
6 changes: 5 additions & 1 deletion example/reader_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion example/reader_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion python/ndpi/ndpi_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
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);
Expand Down
4 changes: 2 additions & 2 deletions src/include/ndpi_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
*
Expand Down
2 changes: 1 addition & 1 deletion src/include/ndpi_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/include/ndpi_typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
20 changes: 16 additions & 4 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

/* ************************************************ */
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
}

Expand Down Expand Up @@ -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);
Expand Down
89 changes: 85 additions & 4 deletions wireshark/ndpi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)


Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 = ""
Expand Down Expand Up @@ -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)

-- ###############################################

Expand Down

0 comments on commit e2ed23a

Please sign in to comment.