194 lines
5.7 KiB
Lua
194 lines
5.7 KiB
Lua
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
|
|
|