summaryrefslogtreecommitdiffstats
path: root/scripts/sniffer-detect.nse
blob: 0621f743fa38f23ae83a5523621b12f514f87219 (plain)
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