145 lines
4.2 KiB
Lua
145 lines
4.2 KiB
Lua
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
|