1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 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
|