diff options
Diffstat (limited to 'scripts/eap-info.nse')
-rw-r--r-- | scripts/eap-info.nse | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/scripts/eap-info.nse b/scripts/eap-info.nse new file mode 100644 index 0000000..5741f4c --- /dev/null +++ b/scripts/eap-info.nse @@ -0,0 +1,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 + |