nDPI/wireshark/sharkfest_scripts/dns_request_reply_ratio.lua
2021-06-11 21:01:27 +02:00

147 lines
4.2 KiB
Lua

--
-- (C) 2021 - ntop.org
--
-- This is going to be an example of a lua script that can be written for cybersecurity reasons.
-- DNS Request/Reply Ratio:
local f_dns = Field.new("dns")
local f_dns_response_flag = Field.new("dns.flags.response")
local f_ip_src = Field.new("ip.src")
local f_ip_dst = Field.new("ip.dst")
--############################################
local function getstring(finfo)
local ok, val = pcall(tostring, finfo)
if not ok then val = "(unknown)" end
return val
end
--############################################
local function processResponse(dns_table, req_or_rep, src, dst)
local key = src .. " -> " .. dst
-- Create the table entry if needed
if not dns_table[key] then
dns_table[key] = {
requests = 0,
replies = 0,
}
end
-- Increase the stats
dns_table[key][req_or_rep] = dns_table[key][req_or_rep] + 1
return dns_table
end
--############################################
local function processPackets(pinfo,tvb, dns_table)
-- Call the function that extracts the field
local dns_traffic = f_dns()
local dns_flag = f_dns_response_flag()
--Check if there is an DNS request or reply
if dns_traffic then
if dns_flag.value == false then
local src = getstring(f_ip_src().value)
local dst = getstring(f_ip_dst().value)
dns_table = processResponse(dns_table, "requests", src, dst)
else
local dst = getstring(f_ip_src().value)
local src = getstring(f_ip_dst().value)
dns_table = processResponse(dns_table, "replies", src, dst)
end
end
return dns_table
end
--############################################
local function dnsReqRepRatio()
-- Declare the window we will use
local tw = TextWindow.new("DNS Request/Reply Ratio")
local dns_table = {}
local tap = Listener.new();
local function removeListener()
-- This way we remove the listener that otherwise will remain running indefinitely
tap:remove();
end
-- We tell the window to call the remove() function when closed
tw:set_atclose(removeListener)
-- This function will be called once for each packet
function tap.packet(pinfo,tvb)
dns_table = processPackets(pinfo,tvb, dns_table)
end
-- This function will be called once every few seconds to update our window
function tap.draw(t)
tw:clear()
local dangerous_flows = {}
local ok_flows = {}
for flow, data in pairs(dns_table) do
local requests = dns_table[flow]["requests"]
local replies = dns_table[flow]["replies"]
local ratio = 0
if replies == 0 then
ratio = 0
else
ratio = requests/replies
end
if ratio ~= 1 then
dangerous_flows[#dangerous_flows + 1] = data
dangerous_flows[#dangerous_flows]["flow"] = flow
dangerous_flows[#dangerous_flows]["ratio"] = ratio
else
ok_flows[#ok_flows + 1] = data
ok_flows[#ok_flows]["flow"] = flow
ok_flows[#ok_flows]["ratio"] = ratio
end
end
if #dangerous_flows > 0 then
tw:append("------------- DETECTED DNS REQUEST/REPLY RATIO -------------\n")
tw:append("TOT SUSPICIOUS FLOWS DETECTED:\t" .. #dangerous_flows .. " -------------\n")
else
tw:append("------------- DNS REQUEST/REPLY RATIO SEEMS FINE -------------\n\n")
end
tw:append("TOTAL DNS FLOWS DETECTED:\t\t" .. #dangerous_flows + #ok_flows .. " -------------\n\n")
for _, data in pairs(dangerous_flows) do
local flow = data["flow"]
local requests = data["requests"]
local replies = data["replies"]
local ratio = data["ratio"]
tw:append(flow .. ":\n\tRatio:\t\t" .. (ratio) .. "\n\tRequests:\t\t" .. requests .. "\n\tReplies:\t\t" .. replies .. "\n\n");
end
end
-- This function will be called whenever a reset is needed
-- e.g. when reloading the capture file
function tap.reset()
tw:clear()
dns_table = {}
end
-- Ensure that all existing packets are processed.
retap_packets()
end
-- Register the menu Entry
register_menu("Sharkfest/DNS Request-Reply Ratio", dnsReqRepRatio, MENU_TOOLS_UNSORTED)