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
|