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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
local eap = require "eap"
local nmap = require "nmap"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Enumerates the authentication methods offered by an EAP (Extensible
Authentication Protocol) authenticator for a given identity or for the
anonymous identity if no argument is passed.
]]
---
-- @usage
-- nmap -e interface --script eap-info [--script-args="eap-info.identity=0-user,eap-info.scan={13,50}"] <target>
--
-- @output
-- Pre-scan script results:
-- | eap-info:
-- | Available authentication methods with identity="anonymous" on interface eth2
-- | true PEAP
-- | true EAP-TTLS
-- | false EAP-TLS
-- |_ false EAP-MSCHAP-V2
--
-- @args eap-info.identity Identity to use for the first step of the authentication methods (if omitted "anonymous" will be used).
-- @args eap-info.scan Table of authentication methods to test, e.g. { 4, 13, 25 } for MD5, TLS and PEAP. Default: TLS, TTLS, PEAP, MSCHAP.
-- @args eap-info.interface Network interface to use for the scan, overrides "-e".
-- @args eap-info.timeout Maximum time allowed for the scan (default 10s). Methods not tested because of timeout will be listed as "unknown".
author = "Riccardo Cecolin"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = { "broadcast", "safe" }
prerule = function()
return nmap.is_privileged()
end
local default_scan = {
eap.eap_t.TLS,
eap.eap_t.TTLS,
eap.eap_t.PEAP,
eap.eap_t.MSCHAP,
}
local UNKNOWN = "unknown"
action = function()
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
local arg_identity = stdnse.get_script_args(SCRIPT_NAME .. ".identity")
local arg_scan = stdnse.get_script_args(SCRIPT_NAME .. ".scan")
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local iface
-- trying with provided interface name
if arg_interface then
iface = nmap.get_interface_info(arg_interface)
end
-- trying with default nmap interface
if not iface then
local iname = nmap.get_interface()
if iname then
iface = nmap.get_interface_info(iname)
end
end
-- failed
if not iface then
return "please specify an interface with -e"
end
stdnse.debug1("iface: %s", iface.device)
local timeout = (arg_timeout or 10) * 1000
stdnse.debug2("timeout: %s", timeout)
local pcap = nmap.new_socket()
pcap:pcap_open(iface.device, 512, true, "ether proto 0x888e")
local identity = { name="anonymous", auth = {}, probe = -1 }
if arg_identity then
identity.name = tostring(arg_identity)
end
local scan
if arg_scan == nil or type(arg_scan) ~= "table" or #arg_scan == 0 then
scan = default_scan
else
scan = arg_scan
end
local valid = false
for i,v in ipairs(scan) do
v = tonumber(v)
if v ~= nil and v < 256 and v > 3 then
stdnse.debug1("selected: %s", eap.eap_str[v] or "unassigned" )
identity.auth[v] = UNKNOWN
valid = true
end
end
if not valid then
return "no valid scan methods provided"
end
local tried_all = false
local start_time = nmap.clock_ms()
eap.send_start(iface)
while(nmap.clock_ms() - start_time < timeout) and not tried_all do
local status, plen, l2_data, l3_data, time = pcap:pcap_receive()
if (status) then
stdnse.debug2("packet size: 0x%x", plen )
local packet = eap.parse(l2_data .. l3_data)
if packet then
stdnse.debug2("packet valid")
-- respond to identity requests, using the same session id
if packet.eap.type == eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
stdnse.debug1("server identity: %s",packet.eap.body.identity)
eap.send_identity_response(iface, packet.eap.id, identity.name)
end
-- respond with NAK to every auth request to enumerate them until we get a failure
if packet.eap.type ~= eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
stdnse.debug1("auth request: %s",eap.eap_str[packet.eap.type])
identity.auth[packet.eap.type] = true
identity.probe = -1
for i,v in pairs(identity.auth) do
stdnse.debug1("identity.auth: %d %s",i,tostring(v))
if v == UNKNOWN then
identity.probe = i
eap.send_nak_response(iface, packet.eap.id, i)
break
end
end
if identity.probe == -1 then tried_all = true end
end
-- retry on failure
if packet.eap.code == eap.code_t.FAILURE then
stdnse.debug1("auth failure")
identity.auth[identity.probe] = false
-- don't give up at the first failure!
-- mac spoofing to avoid to wait too much
local d = string.byte(iface.mac,6)
d = (d + 1) % 256
iface.mac = iface.mac:sub(1,5) .. string.pack("B",d)
tried_all = true
for i,v in pairs(identity.auth) do
if v == UNKNOWN then
tried_all = false
break
end
end
if not tried_all then
eap.send_start(iface)
end
end
else
stdnse.debug1("packet invalid! wrong filter?")
end
end
end
local results = { ["name"] = ("Available authentication methods with identity=\"%s\" on interface %s"):format(identity.name, iface.device) }
for i,v in pairs(identity.auth) do
if v== true then
table.insert(results, 1, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
else
table.insert(results, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
end
end
for i,v in ipairs(results) do
stdnse.debug1("%s", tostring(v))
end
return stdnse.format_output(true, results)
end
|