summaryrefslogtreecommitdiffstats
path: root/scripts/sniffer-detect.nse
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/sniffer-detect.nse144
1 files changed, 144 insertions, 0 deletions
diff --git a/scripts/sniffer-detect.nse b/scripts/sniffer-detect.nse
new file mode 100644
index 0000000..0621f74
--- /dev/null
+++ b/scripts/sniffer-detect.nse
@@ -0,0 +1,144 @@
+local nmap = require "nmap"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+
+description = [[
+Checks if a target on a local Ethernet has its network card in promiscuous mode.
+
+The techniques used are described at
+http://www.securityfriday.com/promiscuous_detection_01.pdf.
+]]
+
+---
+-- @output
+-- Host script results:
+-- |_ sniffer-detect: Likely in promiscuous mode (tests: "11111111")
+
+
+author = "Marek Majkowski"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+
+categories = {"discovery", "intrusive"}
+
+-- okay, we're interested only in hosts that are on our ethernet lan
+hostrule = function(host)
+ if nmap.address_family() ~= 'inet' then
+ stdnse.debug1("is IPv4 compatible only.")
+ return false
+ end
+ if host.directly_connected == true and
+ host.mac_addr ~= nil and
+ host.mac_addr_src ~= nil and
+ host.interface ~= nil then
+ local iface = nmap.get_interface_info(host.interface)
+ if iface and iface.link == 'ethernet' then
+ return true
+ end
+ end
+ return false
+end
+
+local function check (layer2)
+ return string.sub(layer2, 0, 12)
+end
+
+
+do_test = function(dnet, pcap, host, test)
+ local status, length, layer2, layer3
+ local i = 0
+
+ -- ARP requests are send with timeouts: 10ms, 40ms, 90ms
+ -- before each try, we wait at least 100ms
+ -- in summary, this test takes at least 100ms and at most 440ms
+ for i=1,3 do
+ -- flush buffers :), wait quite long.
+ repeat
+ pcap:set_timeout(100)
+ local test = host.mac_addr_src .. host.mac_addr
+ status, length, layer2, layer3 = pcap:pcap_receive()
+ while status and test ~= check(layer2) do
+ status, length, layer2, layer3 = pcap:pcap_receive()
+ end
+ until status ~= true
+ pcap:set_timeout(10 * i*i)
+
+ dnet:ethernet_send(test)
+
+ local test = host.mac_addr_src .. host.mac_addr
+ status, length, layer2, layer3 = pcap:pcap_receive()
+ while status and test ~= check(layer2) do
+ status, length, layer2, layer3 = pcap:pcap_receive()
+ end
+ if status == true then
+ -- the basic idea, was to inform user about time, when we got packet
+ -- so that 1 would mean (0-10ms), 2=(10-40ms) and 3=(40ms-90ms)
+ -- but when we're running this tests on macs, first test is always 2.
+ -- which means that the first answer is dropped.
+ -- for now, just return 1 if test was successful, it's easier
+ -- return(i)
+ return(1)
+ end
+ end
+ return('_')
+end
+
+action = function(host)
+ local dnet = nmap.new_dnet()
+ local pcap = nmap.new_socket()
+ local _
+ local status
+ local results = {
+ ['1_____1_'] = false, -- MacOSX(Tiger.Panther)/Linux/ ?Win98/ WinXP sp2(no pcap)
+ ['1_______'] = false, -- Old Apple/SunOS/3Com
+ ['1___1_1_'] = false, -- MacOSX(Tiger)
+ ['11111111'] = true, -- BSD/Linux/OSX/ (or not promiscuous openwrt )
+ ['1_1___1_'] = false, -- WinXP sp2 + pcap|| win98 sniff || win2k sniff (see below)
+ ['111___1_'] = true, -- WinXP sp2 promisc
+ --['1111__1_'] = true, -- ?Win98 promisc + ??win98 no promisc *not confirmed*
+ }
+ dnet:ethernet_open(host.interface)
+
+ pcap:pcap_open(host.interface, 64, false, "arp")
+
+ local test_static = host.mac_addr_src ..
+ "\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01" ..
+ host.mac_addr_src ..
+ host.bin_ip_src ..
+ "\x00\x00\x00\x00\x00\x00" ..
+ host.bin_ip
+ local t = {
+ "\xff\xff\xff\xff\xff\xff", -- B32 no meaning?
+ "\xff\xff\xff\xff\xff\xfe", -- B31
+ "\xff\xff\x00\x00\x00\x00", -- B16
+ "\xff\x00\x00\x00\x00\x00", -- B8
+ "\x01\x00\x00\x00\x00\x00", -- G
+ "\x01\x00\x5e\x00\x00\x00", -- M0
+ "\x01\x00\x5e\x00\x00\x01", -- M1 no meaning?
+ "\x01\x00\x5e\x00\x00\x03", -- M3
+ }
+ local v
+ local out = {}
+ for _, v in ipairs(t) do
+ out[#out+1] = do_test(dnet, pcap, host, v .. test_static)
+ end
+ out = table.concat(out)
+
+ dnet:ethernet_close()
+ pcap:pcap_close()
+
+ if out == '1_1___1_' then
+ return 'Windows with libpcap installed; may or may not be sniffing (tests: "' .. out .. '")'
+ end
+ if results[out] == false then
+ -- probably not sniffing
+ return
+ end
+ if results[out] == true then
+ -- rather sniffer.
+ return 'Likely in promiscuous mode (tests: "' .. out .. '")'
+ end
+
+ -- results[out] == nil
+ return 'Unknown (tests: "' .. out .. '")'
+end