summaryrefslogtreecommitdiffstats
path: root/scripts/snmp-info.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/snmp-info.nse')
-rw-r--r--scripts/snmp-info.nse145
1 files changed, 145 insertions, 0 deletions
diff --git a/scripts/snmp-info.nse b/scripts/snmp-info.nse
new file mode 100644
index 0000000..bad3f4f
--- /dev/null
+++ b/scripts/snmp-info.nse
@@ -0,0 +1,145 @@
+local datetime = require "datetime"
+local datafiles = require "datafiles"
+local ipOps = require "ipOps"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local snmp = require "snmp"
+local stdnse = require "stdnse"
+local string = require "string"
+local U = require "lpeg-utility"
+local comm = require "comm"
+
+description = [[
+Extracts basic information from an SNMPv3 GET request. The same probe is used
+here as in the service version detection scan.
+]]
+
+---
+--@output
+--161/udp open snmp udp-response ttl 244 ciscoSystems SNMPv3 server (public)
+--| snmp-info:
+--| enterprise: ciscoSystems
+--| engineIDFormat: mac
+--| engineIDData: 00:d4:8c:00:11:22
+--| snmpEngineBoots: 6
+--|_ snmpEngineTime: 358d01h13m46s
+--
+--@xmloutput
+-- <elem key="enterprise">ciscoSystems</elem>
+-- <elem key="engineIDFormat">mac</elem>
+-- <elem key="engineIDData">00:d4:8c:b5:32:bc</elem>
+-- <elem key="snmpEngineBoots">6</elem>
+-- <elem key="snmpEngineTime">358d01h26m34s</elem>
+
+author = "Daniel Miller"
+
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+
+categories = {"default", "version", "safe"}
+
+portrule = shortport.version_port_or_service(161, "snmp", "udp")
+
+-- Lifted from nmap-service-probes:
+local SNMPv3GetRequest = "\x30\x3a\x02\x01\x03\x30\x0f\x02\x02\x4a\x69\x02\x03\0\xff\xe3\x04\x01\x04\x02\x01\x03\x04\x10\x30\x0e\x04\0\x02\x01\0\x02\x01\0\x04\0\x04\0\x04\0\x30\x12\x04\0\x04\0\xa0\x0c\x02\x02\x37\xf0\x02\x01\0\x02\x01\0\x30\0"
+
+-- TODO: This should probably check for version 1 and version 2, since those
+-- can operate on the same port. Right now it's really just "snmp3-info"
+action = function (host, port)
+ local ENTERPRISE_NUMS = nmap.registry.enterprise_numbers
+ if not ENTERPRISE_NUMS then
+ local status
+ status, ENTERPRISE_NUMS = datafiles.parse_file("nselib/data/enterprise_numbers.txt",
+ {[function(l) return tonumber(l:match("^%d+")) end] = "\t(.*)$"})
+ if not status then
+ stdnse.debug1("Couldn't parse enterprise numbers datafile: %s", ENTERPRISE_NUMS)
+ ENTERPRISE_NUMS = {}
+ setmetatable(ENTERPRISE_NUMS, {__index = function(i) return "unknown" end})
+ end
+ nmap.registry.enterprise_numbers = ENTERPRISE_NUMS
+ end
+
+ local response
+ -- Did the service engine already do the hard work?
+ if port.version and port.version.service_fp then
+ -- Probes sent, replies received, but no match.
+ response = U.get_response(port.version.service_fp, "SNMPv3GetRequest")
+ end
+
+ if not response then
+ -- Have to send the probe ourselves
+ local status
+ status, response = comm.exchange(host, port, SNMPv3GetRequest)
+ if not status then
+ stdnse.debug1("Couldn't get a response: %s", response)
+ return nil
+ end
+ end
+
+ local decoded = snmp.decode(response)
+
+ -- Check for SNMP version 3 and msgid 0x4a69 (from the probe)
+ if ((not decoded) or
+ (decoded[1] or false) ~= 3 or
+ (not decoded[2]) or
+ (decoded[2][1] or false) ~= 0x4a69) then
+ stdnse.debug1("Service is not SNMPv3, or packet structure not recognized")
+ return nil
+ end
+
+ -- This really only works for User-based Security Model (USM)
+ if decoded[2][4] ~= 3 then
+ -- TODO: at least report the security model in use
+ stdnse.debug1("SNMP service not using User-based Security Model")
+ return nil
+ end
+
+ -- Decode the msgSecurityParameters octet-string
+ decoded = snmp.decode(decoded[3])
+
+ local output = stdnse.output_table()
+ -- Decode the msgAuthoritativeEngineID octet-string
+ local engineID = decoded[1]
+ local enterprise, pos = string.unpack(">I4", engineID)
+ if enterprise > 0x80000000 then
+ enterprise = enterprise - 0x80000000
+ output.enterprise = ENTERPRISE_NUMS[enterprise]
+ local format, data
+ format, pos = string.unpack("B", engineID, pos)
+ if format == 1 then
+ output.engineIDFormat = "ipv4"
+ output.engineIDData = ipOps.str_to_ip(engineID:sub(pos,pos+3))
+ elseif format == 2 then
+ output.engineIDFormat = "ipv6"
+ output.engineIDData = ipOps.str_to_ip(engineID:sub(pos,pos+15))
+ elseif format == 3 then
+ output.engineIDFormat = "mac"
+ output.engineIDData = stdnse.tohex(engineID:sub(pos,pos+5), {separator=':'})
+ elseif format == 4 then
+ output.engineIDFormat = "text"
+ output.engineIDData = engineID:sub(pos)
+ elseif format == 5 then
+ output.engineIDFormat = "octets"
+ output.engineIDData = stdnse.tohex(engineID:sub(pos))
+ else
+ output.engineIDFormat = "unknown"
+ output.engineIDData = stdnse.tohex(engineID:sub(pos))
+ end
+ else
+ output.enterprise = ENTERPRISE_NUMS[enterprise] or enterprise
+ output.engineIDFormat = "unknown"
+ output.engineIDData = stdnse.tohex(engineID:sub(5))
+ end
+ output.snmpEngineBoots = decoded[2]
+ output.snmpEngineTime = datetime.format_time(decoded[3])
+
+ port.version = port.version or {}
+ port.version.service = "snmp"
+ if port.version.product and port.version.product ~= "SNMPv3 server" then
+ port.version.product = ("%s; %s SNMPv3 server"):format(port.version.product, output.enterprise)
+ else
+ port.version.product = ("%s SNMPv3 server"):format(output.enterprise)
+ end
+ nmap.set_port_version(host, port, "hardmatched")
+
+ return output
+end