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.
Identity to use for the first step of the authentication methods (if omitted “anonymous” will be used).
Table of authentication methods to test, e.g. { 4, 13, 25 } for MD5, TLS and PEAP. Default: TLS, TTLS, PEAP, MSCHAP.
Maximum time allowed for the scan (default 10s). Methods not tested because of timeout will be listed as “unknown”.
Network interface to use for the scan, overrides “-e”.
nmap -e interface --script eap-info [--script-args="eap-info.identity=0-user,eap-info.scan={13,50}"] <target>
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
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