mirror of
https://github.com/nmap/nmap.git
synced 2026-01-19 20:59:01 +00:00
o [NSE] Added a new library for ASN.1 parsing and adapted the SNMP library to
make use of it. Added 5 scripts that use the new libraries:
- snmp-netstat shows listening and connected sockets
- snmp-processes shows process information including name, pid, path and
parameters
- snmp-win32-services shows the names of running Windows services
- snmp-win32-shares shows the names and path of Windows shares
- snmp-win32-software shows a list of installed Windows software
- snmp-win32-users shows a list of local Windows users
This commit is contained in:
13
CHANGELOG
13
CHANGELOG
@@ -2,6 +2,17 @@
|
||||
|
||||
[NOT YET RELEASED]
|
||||
|
||||
o [NSE] Added a new library for ASN.1 parsing and adapted the SNMP library to
|
||||
make use of it. Added 5 scripts that use the new libraries:
|
||||
- snmp-netstat shows listening and connected sockets
|
||||
- snmp-processes shows process information including name, pid, path and
|
||||
parameters
|
||||
- snmp-win32-services shows the names of running Windows services
|
||||
- snmp-win32-shares shows the names and path of Windows shares
|
||||
- snmp-win32-software shows a list of installed Windows software
|
||||
- snmp-win32-users shows a list of local Windows users
|
||||
[Patrik]
|
||||
|
||||
o Qualified an assertion to allow zero-byte sends in Nsock. Without
|
||||
this, an NSE script could cause this assertion failure by doing
|
||||
socket:send(""):
|
||||
@@ -9,7 +20,7 @@ o Qualified an assertion to allow zero-byte sends in Nsock. Without
|
||||
[David]
|
||||
|
||||
o Added a service probe for Logitech SqueezeCenter command line interface
|
||||
[Patrik]
|
||||
[Patrik]
|
||||
|
||||
o Improved PostgreSQL match lines by matching the line of the error to a
|
||||
specific version [Patrik].
|
||||
|
||||
459
nselib/asn1.lua
Normal file
459
nselib/asn1.lua
Normal file
@@ -0,0 +1,459 @@
|
||||
--- ASN1 functions.
|
||||
--
|
||||
-- Large chunks of this code have been ripped right out from snmp.lua
|
||||
--
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
--
|
||||
-- @author Patrik Karlsson
|
||||
--
|
||||
|
||||
-- Version 0.3
|
||||
-- Created 01/12/2010 - v0.1 - Created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/28/2010 - v0.2 - Adapted to create a framework for SNMP, LDAP and future protocols
|
||||
-- Revised 02/02/2010 - v0.3 - Changes: o Re-designed so that ASN1Encoder and ASN1Decoder are separate classes
|
||||
-- o Each script or library should now create it's own Encoder and Decoder instance
|
||||
--
|
||||
|
||||
module(... or "asn1",package.seeall)
|
||||
|
||||
require("bit")
|
||||
|
||||
BERCLASS = {
|
||||
Universal = 0,
|
||||
Application = 64,
|
||||
ContextSpecific = 128,
|
||||
Private = 192
|
||||
}
|
||||
|
||||
--- The decoder class
|
||||
--
|
||||
ASN1Decoder = {
|
||||
|
||||
new = function(self,o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Registers the base simple type decoders
|
||||
--
|
||||
registerBaseDecoders = function(self)
|
||||
self.decoder = {}
|
||||
|
||||
-- Boolean
|
||||
self.decoder["01"] = function( self, encStr, elen, pos )
|
||||
local val = bin.unpack("H", encStr, pos)
|
||||
if val ~= "FF" then
|
||||
return pos, true
|
||||
else
|
||||
return pos, false
|
||||
end
|
||||
end
|
||||
|
||||
-- Integer
|
||||
self.decoder["02"] = function( self, encStr, elen, pos )
|
||||
return self.decodeInt(encStr, elen, pos)
|
||||
end
|
||||
|
||||
-- Octet String
|
||||
self.decoder["04"] = function( self, encStr, elen, pos )
|
||||
return bin.unpack("A" .. elen, encStr, pos)
|
||||
end
|
||||
|
||||
-- Null
|
||||
self.decoder["05"] = function( self, encStr, elen, pos )
|
||||
return pos, false
|
||||
end
|
||||
|
||||
-- Object Identifier
|
||||
self.decoder["06"] = function( self, encStr, elen, pos )
|
||||
return self:decodeOID( encStr, elen, pos )
|
||||
end
|
||||
|
||||
-- Context specific tags
|
||||
--
|
||||
self.decoder["30"] = function( self, encStr, elen, pos )
|
||||
return self:decodeSeq(encStr, elen, pos)
|
||||
end
|
||||
end,
|
||||
|
||||
--- Allows for registration of additional tag decoders
|
||||
--
|
||||
-- @param table containing decoding functions @see tagDecoders
|
||||
registerTagDecoders = function(self, tagDecoders)
|
||||
self:registerBaseDecoders()
|
||||
for k, v in pairs(tagDecoders) do
|
||||
self.decoder[k] = v
|
||||
end
|
||||
end,
|
||||
|
||||
--- Decodes the ASN.1's built-in simple types
|
||||
--
|
||||
-- @param encStr Encoded string.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding
|
||||
-- @return The decoded value(s).
|
||||
decode = function(self, encStr, pos)
|
||||
|
||||
local etype, elen
|
||||
local newpos = pos
|
||||
|
||||
newpos, etype = bin.unpack("H1", encStr, newpos)
|
||||
newpos, elen = self.decodeLength(encStr, newpos)
|
||||
|
||||
if self.decoder[etype] then
|
||||
return self.decoder[etype]( self, encStr, elen, newpos )
|
||||
else
|
||||
stdnse.print_debug("no decoder for etype: " .. etype)
|
||||
return newpos, nil
|
||||
end
|
||||
end,
|
||||
|
||||
---
|
||||
-- Decodes length part of encoded value according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The length of the following value.
|
||||
decodeLength = function(encStr, pos)
|
||||
local elen
|
||||
pos, elen = bin.unpack('C', encStr, pos)
|
||||
if (elen > 128) then
|
||||
elen = elen - 128
|
||||
local elenCalc = 0
|
||||
local elenNext
|
||||
for i = 1, elen do
|
||||
elenCalc = elenCalc * 256
|
||||
pos, elenNext = bin.unpack("C", encStr, pos)
|
||||
elenCalc = elenCalc + elenNext
|
||||
end
|
||||
elen = elenCalc
|
||||
end
|
||||
return pos, elen
|
||||
end,
|
||||
|
||||
---
|
||||
-- Decodes a sequence according to ASN.1 basic encoding rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param len Length of sequence in bytes.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The decoded sequence as a table.
|
||||
decodeSeq = function(self, encStr, len, pos)
|
||||
local seq = {}
|
||||
local sPos = 1
|
||||
local sStr
|
||||
pos, sStr = bin.unpack("A" .. len, encStr, pos)
|
||||
while (sPos < len) do
|
||||
local newSeq
|
||||
sPos, newSeq = self:decode(sStr, sPos)
|
||||
table.insert(seq, newSeq)
|
||||
end
|
||||
return pos, seq
|
||||
end,
|
||||
|
||||
-- Decode one component of an OID from a byte string. 7 bits of the component
|
||||
-- are stored in each octet, most significant first, with the eigth bit set in
|
||||
-- all octets but the last. These encoding rules come from
|
||||
-- http://luca.ntop.org/Teaching/Appunti/asn1.html, section 5.9 OBJECT
|
||||
-- IDENTIFIER.
|
||||
decode_oid_component = function(encStr, pos)
|
||||
local octet
|
||||
local n = 0
|
||||
|
||||
repeat
|
||||
pos, octet = bin.unpack("C", encStr, pos)
|
||||
n = n * 128 + bit.band(0x7F, octet)
|
||||
until octet < 128
|
||||
|
||||
return pos, n
|
||||
end,
|
||||
|
||||
--- Decodes an OID from a sequence of bytes.
|
||||
--
|
||||
-- @param encStr Encoded string.
|
||||
-- @param len Length of sequence in bytes.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The OID as an array.
|
||||
decodeOID = function(self, encStr, len, pos)
|
||||
local last
|
||||
local oid = {}
|
||||
local octet
|
||||
|
||||
last = pos + len - 1
|
||||
if pos <= last then
|
||||
oid._snmp = '06'
|
||||
pos, octet = bin.unpack("C", encStr, pos)
|
||||
oid[2] = math.mod(octet, 40)
|
||||
octet = octet - oid[2]
|
||||
oid[1] = octet/40
|
||||
end
|
||||
|
||||
while pos <= last do
|
||||
local c
|
||||
pos, c = self.decode_oid_component(encStr, pos)
|
||||
oid[#oid + 1] = c
|
||||
end
|
||||
|
||||
return pos, oid
|
||||
end,
|
||||
|
||||
---
|
||||
-- Decodes length part of encoded value according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The length of the following value.
|
||||
decodeLength = function(encStr, pos)
|
||||
local elen
|
||||
pos, elen = bin.unpack('C', encStr, pos)
|
||||
if (elen > 128) then
|
||||
elen = elen - 128
|
||||
local elenCalc = 0
|
||||
local elenNext
|
||||
for i = 1, elen do
|
||||
elenCalc = elenCalc * 256
|
||||
pos, elenNext = bin.unpack("C", encStr, pos)
|
||||
elenCalc = elenCalc + elenNext
|
||||
end
|
||||
elen = elenCalc
|
||||
end
|
||||
return pos, elen
|
||||
end,
|
||||
|
||||
---
|
||||
-- Decodes an Integer according to ASN.1 basic encoding rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param len Length of integer in bytes.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The decoded integer.
|
||||
decodeInt = function(encStr, len, pos)
|
||||
local hexStr
|
||||
pos, hexStr = bin.unpack("H" .. len, encStr, pos)
|
||||
local value = tonumber(hexStr, 16)
|
||||
if (value >= math.pow(256, len)/2) then
|
||||
value = value - math.pow(256, len)
|
||||
end
|
||||
return pos, value
|
||||
end,
|
||||
|
||||
---
|
||||
-- Decodes an SNMP packet or a part of it according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The decoded value(s).
|
||||
dec = function(self, encStr, pos)
|
||||
local result
|
||||
local _
|
||||
_, result = self:decode(encStr, pos)
|
||||
return result
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
--- The encoder class
|
||||
--
|
||||
ASN1Encoder = {
|
||||
|
||||
new = function(self,o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
---
|
||||
-- Encodes an ASN1 sequence
|
||||
encodeSeq = function(self, seqData)
|
||||
return bin.pack('HAA' , '30', self.encodeLength(string.len(seqData)), seqData)
|
||||
end,
|
||||
|
||||
---
|
||||
-- Encodes a given value according to ASN.1 basic encoding rules for SNMP
|
||||
-- packet creation.
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded value.
|
||||
encode = function(self, val)
|
||||
local vtype = type(val)
|
||||
|
||||
if self.encoder[vtype] then
|
||||
return self.encoder[vtype](self,val)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
return ''
|
||||
end,
|
||||
|
||||
--- Allows for registration of additional tag encoders
|
||||
--
|
||||
-- @param table containing encoding functions @see tagEncoders
|
||||
registerTagEncoders = function(self, tagEncoders)
|
||||
self:registerBaseEncoders()
|
||||
for k, v in pairs(tagEncoders) do
|
||||
self.encoder[k] = v
|
||||
end
|
||||
end,
|
||||
|
||||
-- ASN.1 Simple types encoders
|
||||
registerBaseEncoders = function(self)
|
||||
self.encoder = {}
|
||||
|
||||
-- Bolean encoder
|
||||
self.encoder['boolean'] = function( self, val )
|
||||
if val then
|
||||
return bin.pack('H','01 01 FF')
|
||||
else
|
||||
return bin.pack('H', '01 01 00')
|
||||
end
|
||||
end
|
||||
|
||||
-- Integer encoder
|
||||
self.encoder['number'] = function( self, val )
|
||||
local ival = self.encodeInt(val)
|
||||
local len = self.encodeLength(string.len(ival))
|
||||
return bin.pack('HAA', '02', len, ival)
|
||||
end
|
||||
|
||||
-- Octet String encoder
|
||||
self.encoder['string'] = function( self, val )
|
||||
local len = self.encodeLength(string.len(val))
|
||||
return bin.pack('HAA', '04', len, val)
|
||||
end
|
||||
|
||||
-- Null encoder
|
||||
self.encoder['nil'] = function( self, val )
|
||||
return bin.pack('H', '05 00')
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
-- Encode one component of an OID as a byte string. 7 bits of the component are
|
||||
-- stored in each octet, most significant first, with the eigth bit set in all
|
||||
-- octets but the last. These encoding rules come from
|
||||
-- http://luca.ntop.org/Teaching/Appunti/asn1.html, section 5.9 OBJECT
|
||||
-- IDENTIFIER.
|
||||
encode_oid_component = function(n)
|
||||
local parts = {}
|
||||
parts[1] = string.char(bit.mod(n, 128))
|
||||
while n >= 128 do
|
||||
n = bit.rshift(n, 7)
|
||||
parts[#parts + 1] = string.char(bit.mod(n, 128) + 0x80)
|
||||
end
|
||||
return string.reverse(table.concat(parts))
|
||||
end,
|
||||
|
||||
---
|
||||
-- Encodes an Integer according to ASN.1 basic encoding rules.
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded integer.
|
||||
encodeInt = function(val)
|
||||
local lsb = 0
|
||||
if val > 0 then
|
||||
local valStr = ""
|
||||
while (val > 0) do
|
||||
lsb = math.mod(val, 256)
|
||||
valStr = valStr .. bin.pack("C", lsb)
|
||||
val = math.floor(val/256)
|
||||
end
|
||||
if lsb > 127 then -- two's complement collision
|
||||
valStr = valStr .. bin.pack("H", "00")
|
||||
end
|
||||
|
||||
return string.reverse(valStr)
|
||||
elseif val < 0 then
|
||||
local i = 1
|
||||
local tcval = val + 256 -- two's complement
|
||||
while tcval <= 127 do
|
||||
tcval = tcval + (math.pow(256, i) * 255)
|
||||
i = i+1
|
||||
end
|
||||
local valStr = ""
|
||||
while (tcval > 0) do
|
||||
lsb = math.mod(tcval, 256)
|
||||
valStr = valStr .. bin.pack("C", lsb)
|
||||
tcval = math.floor(tcval/256)
|
||||
end
|
||||
return string.reverse(valStr)
|
||||
else -- val == 0
|
||||
return bin.pack("x")
|
||||
end
|
||||
end,
|
||||
|
||||
---
|
||||
-- Encodes the length part of a ASN.1 encoding triplet using the "primitive,
|
||||
-- definite-length" method.
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded length value.
|
||||
encodeLength = function(len)
|
||||
if len < 128 then
|
||||
return string.char(len)
|
||||
else
|
||||
local parts = {}
|
||||
|
||||
while len > 0 do
|
||||
parts[#parts + 1] = string.char(bit.mod(len, 256))
|
||||
len = bit.rshift(len, 8)
|
||||
end
|
||||
|
||||
assert(#parts < 128)
|
||||
return string.char(#parts + 0x80) .. string.reverse(table.concat(parts))
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
---
|
||||
-- Converts a BER encoded type to a numeric value
|
||||
-- This allows it to be used in the encoding function
|
||||
--
|
||||
-- @param class number - see <code>BERCLASS<code>
|
||||
-- @param constructed boolean (true if constructed, false if primitive)
|
||||
-- @param number numeric
|
||||
-- @return number to be used with <code>encode</code>
|
||||
function BERtoInt(class, constructed, number)
|
||||
|
||||
local asn1_type = class + number
|
||||
|
||||
if constructed == true then
|
||||
asn1_type = asn1_type + 32
|
||||
end
|
||||
|
||||
return asn1_type
|
||||
end
|
||||
|
||||
---
|
||||
-- Converts an integer to a BER encoded type table
|
||||
--
|
||||
-- @param i number containing the value to decode
|
||||
-- @return table with the following entries <code>class</code>, <code>constructed</code>,
|
||||
-- <code>primitive</code> and <code>number</code>
|
||||
function intToBER( i )
|
||||
local ber = {}
|
||||
|
||||
if bit.band( i, BERCLASS.Application ) == BERCLASS.Application then
|
||||
ber.class = BERCLASS.Application
|
||||
elseif bit.band( i, BERCLASS.ContextSpecific ) == BERCLASS.ContextSpecific then
|
||||
ber.class = BERCLASS.ContextSpecific
|
||||
elseif bit.band( i, BERCLASS.Private ) == BERCLASS.Private then
|
||||
ber.class = BERCLASS.Private
|
||||
else
|
||||
ber.class = BERCLASS.Universal
|
||||
end
|
||||
if bit.band( i, 32 ) == 32 then
|
||||
ber.constructed = true
|
||||
ber.number = i - ber.class - 32
|
||||
else
|
||||
ber.primitive = true
|
||||
ber.number = i - ber.class
|
||||
end
|
||||
return ber
|
||||
end
|
||||
|
||||
|
||||
436
nselib/snmp.lua
436
nselib/snmp.lua
@@ -3,254 +3,158 @@
|
||||
-- @args snmpcommunity The community string to use. If not given, it is
|
||||
-- <code>"public"</code>, or whatever is passed to <code>buildPacket</code>.
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
module(... or "snmp",package.seeall)
|
||||
|
||||
|
||||
require("bit")
|
||||
require("asn1")
|
||||
|
||||
---
|
||||
-- Encodes an Integer according to ASN.1 basic encoding rules.
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded integer.
|
||||
local function encodeInt(val)
|
||||
local lsb = 0
|
||||
if val > 0 then
|
||||
local valStr = ""
|
||||
while (val > 0) do
|
||||
lsb = math.mod(val, 256)
|
||||
valStr = valStr .. bin.pack("C", lsb)
|
||||
val = math.floor(val/256)
|
||||
end
|
||||
if lsb > 127 then -- two's complement collision
|
||||
valStr = valStr .. bin.pack("H", "00")
|
||||
end
|
||||
-- SNMP ASN.1 Encoders
|
||||
local tagEncoder = {}
|
||||
|
||||
return string.reverse(valStr)
|
||||
elseif val < 0 then
|
||||
local i = 1
|
||||
local tcval = val + 256 -- two's complement
|
||||
while tcval <= 127 do
|
||||
tcval = tcval + (math.pow(256, i) * 255)
|
||||
i = i+1
|
||||
end
|
||||
local valStr = ""
|
||||
while (tcval > 0) do
|
||||
lsb = math.mod(tcval, 256)
|
||||
valStr = valStr .. bin.pack("C", lsb)
|
||||
tcval = math.floor(tcval/256)
|
||||
end
|
||||
return string.reverse(valStr)
|
||||
else -- val == 0
|
||||
return bin.pack("x")
|
||||
end
|
||||
-- Override the boolean encoder
|
||||
tagEncoder['boolean'] = function(self, val)
|
||||
return bin.pack('H', '05 00')
|
||||
end
|
||||
|
||||
-- Complex tag encoders
|
||||
tagEncoder['table'] = function(self, val)
|
||||
if val._snmp == '06' then -- OID
|
||||
local oidStr = string.char(val[1]*40 + val[2])
|
||||
for i = 3, #val do
|
||||
oidStr = oidStr .. self.encode_oid_component(val[i])
|
||||
end
|
||||
return bin.pack("HAA", '06', self.encodeLength(#oidStr), oidStr)
|
||||
|
||||
---
|
||||
-- Encodes the length part of a ASN.1 encoding triplet using the "primitive,
|
||||
-- definite-length" method.
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded length value.
|
||||
local function encodeLength(len)
|
||||
if len < 128 then
|
||||
return string.char(len)
|
||||
else
|
||||
local parts = {}
|
||||
elseif (val._snmp == '40') then -- ipAddress
|
||||
return bin.pack("HC4", '40 04', unpack(val))
|
||||
|
||||
-- counter or gauge or timeticks or opaque
|
||||
elseif (val._snmp == '41' or val._snmp == '42' or val._snmp == '43' or val._snmp == '44') then
|
||||
local val = self:encodeInt(val[1])
|
||||
return bin.pack("HAA", val._snmp, self.encodeLength(string.len(val)), val)
|
||||
end
|
||||
|
||||
local encVal = ""
|
||||
for _, v in ipairs(val) do
|
||||
encVal = encVal .. self:encode(v) -- todo: buffer?
|
||||
end
|
||||
|
||||
while len > 0 do
|
||||
parts[#parts + 1] = string.char(bit.mod(len, 256))
|
||||
len = bit.rshift(len, 8)
|
||||
end
|
||||
|
||||
assert(#parts < 128)
|
||||
return string.char(#parts + 0x80) .. string.reverse(table.concat(parts))
|
||||
end
|
||||
local tableType = bin.pack("H", "30")
|
||||
if (val["_snmp"]) then
|
||||
tableType = bin.pack("H", val["_snmp"])
|
||||
end
|
||||
return bin.pack('AAA', tableType, self.encodeLength(string.len(encVal)), encVal)
|
||||
end
|
||||
|
||||
|
||||
-- Encode one component of an OID as a byte string. 7 bits of the component are
|
||||
-- stored in each octet, most significant first, with the eigth bit set in all
|
||||
-- octets but the last. These encoding rules come from
|
||||
-- http://luca.ntop.org/Teaching/Appunti/asn1.html, section 5.9 OBJECT
|
||||
-- IDENTIFIER.
|
||||
local function encode_oid_component(n)
|
||||
local parts = {}
|
||||
parts[1] = string.char(bit.mod(n, 128))
|
||||
while n >= 128 do
|
||||
n = bit.rshift(n, 7)
|
||||
parts[#parts + 1] = string.char(bit.mod(n, 128) + 0x80)
|
||||
end
|
||||
return string.reverse(table.concat(parts))
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Encodes a given value according to ASN.1 basic encoding rules for SNMP
|
||||
-- packet creation.
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded value.
|
||||
function encode(val)
|
||||
local vtype = type(val)
|
||||
if (vtype == 'number') then
|
||||
local ival = encodeInt(val)
|
||||
local len = encodeLength(string.len(ival))
|
||||
return bin.pack('HAA', '02', len, ival)
|
||||
end
|
||||
if (vtype == 'string') then
|
||||
local len = encodeLength(string.len(val))
|
||||
return bin.pack('HAA', '04', len, val)
|
||||
end
|
||||
if (vtype == 'nil' or vtype == 'boolean') then
|
||||
return bin.pack('H', '05 00')
|
||||
end
|
||||
if (vtype == 'table') then -- complex data types
|
||||
if val._snmp == '06' then -- OID
|
||||
local oidStr = string.char(val[1]*40 + val[2])
|
||||
for i = 3, #val do
|
||||
oidStr = oidStr .. encode_oid_component(val[i])
|
||||
end
|
||||
return bin.pack("HAA", '06', encodeLength(#oidStr), oidStr)
|
||||
elseif (val._snmp == '40') then -- ipAddress
|
||||
return bin.pack("HC4", '40 04', unpack(val))
|
||||
elseif (val._snmp == '41') then -- counter
|
||||
local cnt = encodeInt(val[1])
|
||||
return bin.pack("HAA", val._snmp, encodeLength(string.len(cnt)), cnt)
|
||||
elseif (val._snmp == '42') then -- gauge
|
||||
local gauge = encodeInt(val[1])
|
||||
return bin.pack("HAA", val._snmp, encodeLength(string.len(gauge)), gauge)
|
||||
elseif (val._snmp == '43') then -- timeticks
|
||||
local ticks = encodeInt(val[1])
|
||||
return bin.pack("HAA", val._snmp, encodeLength(string.len(ticks)), ticks)
|
||||
elseif (val._snmp == '44') then -- opaque
|
||||
return bin.pack("HAA", val._snmp, encodeLength(string.len(val[1])), val[1])
|
||||
end
|
||||
local encVal = ""
|
||||
for _, v in ipairs(val) do
|
||||
encVal = encVal .. encode(v) -- todo: buffer?
|
||||
end
|
||||
local tableType = bin.pack("H", "30")
|
||||
if (val["_snmp"]) then
|
||||
tableType = bin.pack("H", val["_snmp"])
|
||||
end
|
||||
return bin.pack('AAA', tableType, encodeLength(string.len(encVal)), encVal)
|
||||
end
|
||||
return ''
|
||||
local vtype = type(val)
|
||||
local encoder = asn1.ASN1Encoder:new()
|
||||
encoder:registerTagEncoders( tagEncoder )
|
||||
|
||||
|
||||
local encVal = encoder:encode(val)
|
||||
|
||||
if encVal then
|
||||
return encVal
|
||||
end
|
||||
|
||||
return ''
|
||||
end
|
||||
|
||||
-- SNMP ASN.1 Decoders
|
||||
local tagDecoder = {}
|
||||
|
||||
---
|
||||
-- Decodes length part of encoded value according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The length of the following value.
|
||||
local function decodeLength(encStr, pos)
|
||||
local elen
|
||||
pos, elen = bin.unpack('C', encStr, pos)
|
||||
if (elen > 128) then
|
||||
elen = elen - 128
|
||||
local elenCalc = 0
|
||||
local elenNext
|
||||
for i = 1, elen do
|
||||
elenCalc = elenCalc * 256
|
||||
pos, elenNext = bin.unpack("C", encStr, pos)
|
||||
elenCalc = elenCalc + elenNext
|
||||
end
|
||||
elen = elenCalc
|
||||
end
|
||||
return pos, elen
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Decodes an Integer according to ASN.1 basic encoding rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param len Length of integer in bytes.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The decoded integer.
|
||||
local function decodeInt(encStr, len, pos)
|
||||
local hexStr
|
||||
pos, hexStr = bin.unpack("H" .. len, encStr, pos)
|
||||
local value = tonumber(hexStr, 16)
|
||||
if (value >= math.pow(256, len)/2) then
|
||||
value = value - math.pow(256, len)
|
||||
end
|
||||
return pos, value
|
||||
end
|
||||
|
||||
-- Decode one component of an OID from a byte string. 7 bits of the component
|
||||
-- are stored in each octet, most significant first, with the eigth bit set in
|
||||
-- all octets but the last. These encoding rules come from
|
||||
-- http://luca.ntop.org/Teaching/Appunti/asn1.html, section 5.9 OBJECT
|
||||
-- IDENTIFIER.
|
||||
local function decode_oid_component(encStr, pos)
|
||||
local octet
|
||||
local n = 0
|
||||
|
||||
repeat
|
||||
pos, octet = bin.unpack("C", encStr, pos)
|
||||
n = n * 128 + bit.band(0x7F, octet)
|
||||
until octet < 128
|
||||
|
||||
return pos, n
|
||||
end
|
||||
|
||||
--- Decodes an OID from a sequence of bytes.
|
||||
-- Application specific tags
|
||||
--
|
||||
-- @param encStr Encoded string.
|
||||
-- @param len Length of sequence in bytes.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The OID as an array.
|
||||
local function decodeOID(encStr, len, pos)
|
||||
local last
|
||||
local oid = {}
|
||||
local octet
|
||||
-- IP Address
|
||||
|
||||
last = pos + len - 1
|
||||
if pos <= last then
|
||||
oid._snmp = '06'
|
||||
pos, octet = bin.unpack("C", encStr, pos)
|
||||
oid[2] = math.mod(octet, 40)
|
||||
octet = octet - oid[2]
|
||||
oid[1] = octet/40
|
||||
end
|
||||
|
||||
while pos <= last do
|
||||
local c
|
||||
pos, c = decode_oid_component(encStr, pos)
|
||||
oid[#oid + 1] = c
|
||||
end
|
||||
|
||||
return pos, oid
|
||||
tagDecoder["40"] = function( self, encStr, elen, pos )
|
||||
local ip = {}
|
||||
pos, ip[1], ip[2], ip[3], ip[4] = bin.unpack("C4", encStr, pos)
|
||||
ip._snmp = '40'
|
||||
return pos, ip
|
||||
end
|
||||
|
||||
---
|
||||
-- Decodes a sequence according to ASN.1 basic encoding rules.
|
||||
-- @param encStr Encoded string.
|
||||
-- @param len Length of sequence in bytes.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding.
|
||||
-- @return The decoded sequence as a table.
|
||||
local function decodeSeq(encStr, len, pos)
|
||||
local seq = {}
|
||||
local sPos = 1
|
||||
local i = 1
|
||||
local sStr
|
||||
pos, sStr = bin.unpack("A" .. len, encStr, pos)
|
||||
while (sPos < len) do
|
||||
local newSeq
|
||||
sPos, newSeq = decode(sStr, sPos)
|
||||
table.insert(seq, newSeq)
|
||||
i = i + 1
|
||||
end
|
||||
return pos, seq
|
||||
-- Counter
|
||||
tagDecoder["41"] = function( self, encStr, elen, pos )
|
||||
local tbl = {}
|
||||
pos, tbl[1] = self.decodeInt(encStr, elen, pos)
|
||||
tbl._snmp = '41'
|
||||
return pos, tbl
|
||||
end
|
||||
|
||||
-- Gauge
|
||||
tagDecoder["42"] = function( self, encStr, elen, pos )
|
||||
local tbl = {}
|
||||
pos, tbl[1] = self.decodeInt(encStr, elen, pos)
|
||||
tbl._snmp = '41'
|
||||
return pos, tbl
|
||||
end
|
||||
|
||||
-- TimeTicks
|
||||
tagDecoder["43"] = function( self, encStr, elen, pos )
|
||||
local tbl = {}
|
||||
pos, tbl[1] = self.decodeInt(encStr, elen, pos)
|
||||
tbl._snmp = '41'
|
||||
return pos, tbl
|
||||
end
|
||||
|
||||
-- Opaque
|
||||
tagDecoder["44"] = function( self, encStr, elen, pos )
|
||||
local tbl = {}
|
||||
pos, tbl[1] = self.decodeInt(encStr, elen, pos)
|
||||
tbl._snmp = '41'
|
||||
return pos, tbl
|
||||
end
|
||||
|
||||
-- Context specific tags
|
||||
--
|
||||
tagDecoder["A0"] = function( self, encStr, elen, pos )
|
||||
local seq
|
||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = "A0"
|
||||
return pos, seq
|
||||
end
|
||||
|
||||
tagDecoder["A1"] = function( self, encStr, elen, pos )
|
||||
local seq
|
||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = "A1"
|
||||
return pos, seq
|
||||
end
|
||||
|
||||
tagDecoder["A2"] = function( self, encStr, elen, pos )
|
||||
local seq
|
||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = "A2"
|
||||
return pos, seq
|
||||
end
|
||||
|
||||
tagDecoder["A3"] = function( self, encStr, elen, pos )
|
||||
local seq
|
||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = "A3"
|
||||
return pos, seq
|
||||
end
|
||||
|
||||
tagDecoder["A4"] = function( self, encStr, elen, pos )
|
||||
local seq
|
||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = "A4"
|
||||
return pos, seq
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Decodes an SNMP packet or a part of it according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
@@ -259,81 +163,10 @@ end
|
||||
-- @return The position after decoding
|
||||
-- @return The decoded value(s).
|
||||
function decode(encStr, pos)
|
||||
local etype, elen
|
||||
pos, etype = bin.unpack("H1", encStr, pos)
|
||||
pos, elen = decodeLength(encStr, pos)
|
||||
if (etype == "02") then -- INTEGER
|
||||
return decodeInt(encStr, elen, pos)
|
||||
|
||||
elseif (etype == "04") then -- STRING
|
||||
return bin.unpack("A" .. elen, encStr, pos)
|
||||
|
||||
elseif (etype == "05") then -- NULL
|
||||
return pos, false
|
||||
local decoder = asn1.ASN1Decoder:new()
|
||||
decoder:registerTagDecoders( tagDecoder )
|
||||
|
||||
elseif (etype == "06") then -- OID
|
||||
return decodeOID( encStr, elen, pos )
|
||||
|
||||
elseif (etype == "30") then -- sequence
|
||||
local seq
|
||||
pos, seq = decodeSeq(encStr, elen, pos)
|
||||
return pos, seq
|
||||
|
||||
elseif (etype == "A0") then -- getReq
|
||||
local seq
|
||||
pos, seq = decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = etype
|
||||
return pos, seq
|
||||
|
||||
elseif (etype == "A1") then -- getNextReq
|
||||
local seq
|
||||
pos, seq = decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = etype
|
||||
return pos, seq
|
||||
|
||||
elseif (etype == "A2") then -- getResponse
|
||||
local seq
|
||||
pos, seq = decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = etype
|
||||
return pos, seq
|
||||
|
||||
elseif (etype == "A3") then -- setReq
|
||||
local seq
|
||||
pos, seq = decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = etype
|
||||
return pos, seq
|
||||
elseif (etype == "A4") then -- Trap
|
||||
local seq
|
||||
pos, seq = decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = etype
|
||||
return pos, seq
|
||||
elseif (etype == '40') then -- App: IP-Address
|
||||
local ip = {}
|
||||
pos, ip[1], ip[2], ip[3], ip[4] = bin.unpack("C4", encStr, pos)
|
||||
ip._snmp = '40'
|
||||
return pos, ip
|
||||
elseif (etype == '41') then -- App: counter
|
||||
local cnt = {}
|
||||
pos, cnt[1] = decodeInt(encStr, elen, pos)
|
||||
cnt._snmp = '41'
|
||||
return pos, cnt
|
||||
elseif (etype == '42') then -- App: gauge
|
||||
local gauge = {}
|
||||
pos, gauge[1] = decodeInt(encStr, elen, pos)
|
||||
gauge._snmp = '42'
|
||||
return pos, gauge
|
||||
elseif (etype == '43') then -- App: TimeTicks
|
||||
local ticks = {}
|
||||
pos, ticks[1] = decodeInt(encStr, elen, pos)
|
||||
ticks._snmp = '43'
|
||||
return pos, ticks
|
||||
elseif (etype == '44') then -- App: opaque
|
||||
local opaque = {}
|
||||
pos, opaque[1] = bin.unpack("A" .. elen, encStr, pos)
|
||||
opaque._snmp = '44'
|
||||
return pos, opaque
|
||||
end
|
||||
return pos, nil
|
||||
return decoder:decode( encStr, pos )
|
||||
end
|
||||
|
||||
---
|
||||
@@ -416,7 +249,7 @@ end
|
||||
function buildGetNextRequest(options, ...)
|
||||
if not options then options = {} end
|
||||
|
||||
if not options.reqId then options.reqId = math.mod(nmap.clock_ms(), 65000) end
|
||||
if not options.reqId then options.reqId = math.mod(nmap.clock_ms(), 65000) end
|
||||
if not options.err then options.err = 0 end
|
||||
if not options.errIdx then options.errIdx = 0 end
|
||||
|
||||
@@ -431,7 +264,7 @@ function buildGetNextRequest(options, ...)
|
||||
payload[i] = {}
|
||||
payload[i][1] = select(i, ...)
|
||||
if type(payload[i][1]) == "string" then
|
||||
payload[i][1] = str2oid(payload[i][1])
|
||||
payload[i][1] = str2oid(payload[i][1])
|
||||
end
|
||||
payload[i][2] = false
|
||||
end
|
||||
@@ -648,8 +481,11 @@ end
|
||||
-- @param response SNMP Response (will be decoded if necessary).
|
||||
-- @return First decoded value of the response.
|
||||
function fetchFirst(response)
|
||||
local result = fetchResponseValues(response)
|
||||
if type(result) == "table" and result[1] and result[1][1] then return result[1][1]
|
||||
else return nil
|
||||
end
|
||||
local result = fetchResponseValues(response)
|
||||
|
||||
if type(result) == "table" and result[1] and result[1][1] then
|
||||
return result[1][1]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
145
scripts/snmp-netstat.nse
Normal file
145
scripts/snmp-netstat.nse
Normal file
@@ -0,0 +1,145 @@
|
||||
description = [[
|
||||
Attempts to query SNMP for a netstat like output
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- | snmp-netstat:
|
||||
-- | TCP 0.0.0.0:21 0.0.0.0:2256
|
||||
-- | TCP 0.0.0.0:80 0.0.0.0:8218
|
||||
-- | TCP 0.0.0.0:135 0.0.0.0:53285
|
||||
-- | TCP 0.0.0.0:389 0.0.0.0:38990
|
||||
-- | TCP 0.0.0.0:445 0.0.0.0:49158
|
||||
-- | TCP 127.0.0.1:389 127.0.0.1:1045
|
||||
-- | TCP 127.0.0.1:389 127.0.0.1:1048
|
||||
-- | UDP 192.168.56.3:137 *:*
|
||||
-- | UDP 192.168.56.3:138 *:*
|
||||
-- | UDP 192.168.56.3:389 *:*
|
||||
-- |_ UDP 192.168.56.3:464 *:*
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "discovery", "safe"}
|
||||
dependencies = {"snmp-brute"}
|
||||
|
||||
-- Version 0.1
|
||||
-- Created 01/19/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
|
||||
require "shortport"
|
||||
require "snmp"
|
||||
|
||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
|
||||
--- Walks the MIB Tree
|
||||
--
|
||||
-- @param socket socket already connected to the server
|
||||
-- @base_oid string containing the base object ID to walk
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmp_walk( socket, base_oid )
|
||||
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
|
||||
while ( true ) do
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
|
||||
|
||||
try(socket:send(payload))
|
||||
response = try( socket:receive_bytes(1) )
|
||||
|
||||
snmpdata = snmp.fetchResponseValues( response )
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
local lip = oid:match( "^" .. base_oid .. "%.(%d+%.%d+%.%d+%.%d+)") or ""
|
||||
local lport = oid:match( "^" .. base_oid .. "%.%d+%.%d+%.%d+%.%d+%.(%d+)")
|
||||
local fip = oid:match( "^" .. base_oid .. "%.%d+%.%d+%.%d+%.%d+%.%d+%.(%d+%.%d+%.%d+%.%d+)") or "*:*"
|
||||
local fport = oid:match( "^" .. base_oid .. "%.%d+%.%d+%.%d+%.%d+%.%d+%.%d+%.%d+%.%d+%.%d+%.(%d+)")
|
||||
|
||||
if lport and lport ~= "0" then
|
||||
lip = lip .. ":" .. lport
|
||||
end
|
||||
|
||||
if fport and fport ~= "0" then
|
||||
fip = fip .. ":" .. fport
|
||||
end
|
||||
|
||||
|
||||
value = string.format("%-20s %s", lip, fip )
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
end
|
||||
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return snmp_table
|
||||
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
function process_answer( tbl, prefix )
|
||||
|
||||
local new_tab = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
table.insert( new_tab, string.format( "%-4s %s", prefix, v.value ) )
|
||||
end
|
||||
|
||||
return new_tab
|
||||
|
||||
end
|
||||
|
||||
function table_merge( t1, t2 )
|
||||
for _, v in ipairs(t2) do
|
||||
table.insert(t1, v)
|
||||
end
|
||||
|
||||
return t1
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local tcp_oid = "1.3.6.1.2.1.6.13.1.1"
|
||||
local udp_oid = "1.3.6.1.2.1.7.5.1.1"
|
||||
local netstat = {}
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
local tcp = snmp_walk( socket, tcp_oid )
|
||||
local udp = snmp_walk( socket, udp_oid )
|
||||
|
||||
if ( tcp == nil ) or ( #tcp == 0 ) or ( udp==nil ) or ( #udp == 0 ) then
|
||||
return
|
||||
end
|
||||
|
||||
tcp = process_answer(tcp, "TCP")
|
||||
udp = process_answer(udp, "UDP")
|
||||
netstat = table_merge( tcp, udp )
|
||||
|
||||
nmap.set_port_state(host, port, "open")
|
||||
socket:close()
|
||||
|
||||
return stdnse.format_output( true, netstat )
|
||||
end
|
||||
|
||||
176
scripts/snmp-processes.nse
Normal file
176
scripts/snmp-processes.nse
Normal file
@@ -0,0 +1,176 @@
|
||||
description = [[
|
||||
Attempts to enumerate running processes through SNMP
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- | snmp-processes:
|
||||
-- | System Idle Process
|
||||
-- | PID: 1
|
||||
-- | System
|
||||
-- | PID: 4
|
||||
-- | smss.exe
|
||||
-- | Path: \SystemRoot\System32\
|
||||
-- | PID: 256
|
||||
-- | csrss.exe
|
||||
-- | Path: C:\WINDOWS\system32\
|
||||
-- | Params: ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserS
|
||||
-- | PID: 308
|
||||
-- | winlogon.exe
|
||||
-- | PID: 332
|
||||
-- | services.exe
|
||||
-- | Path: C:\WINDOWS\system32\
|
||||
-- | PID: 380
|
||||
-- | lsass.exe
|
||||
-- | Path: C:\WINDOWS\system32\
|
||||
-- |_ PID: 392
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "discovery", "safe"}
|
||||
dependencies = {"snmp-brute"}
|
||||
|
||||
-- Version 0.3
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/19/2010 - v0.2 - fixed loop that would occure if a mib did not exist
|
||||
-- Revised 01/19/2010 - v0.2 - removed debugging output and renamed file
|
||||
|
||||
require "shortport"
|
||||
require "snmp"
|
||||
|
||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
|
||||
--- Walks the MIB Tree
|
||||
--
|
||||
-- @param socket socket already connected to the server
|
||||
-- @base_oid string containing the base object ID to walk
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmp_walk( socket, base_oid )
|
||||
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
|
||||
while ( true ) do
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
|
||||
|
||||
try(socket:send(payload))
|
||||
response = try( socket:receive_bytes(1) )
|
||||
|
||||
snmpdata = snmp.fetchResponseValues( response )
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
end
|
||||
|
||||
socket:close()
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return snmp_table
|
||||
|
||||
end
|
||||
|
||||
--- Gets a value for the specified oid
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @param oid string containing the object id for which the value should be extracted
|
||||
-- @return value of relevant type or nil if oid was not found
|
||||
function get_value_from_table( tbl, oid )
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
if v.oid == oid then
|
||||
return v.value
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
function process_answer( tbl )
|
||||
|
||||
local swrun_name = "1.3.6.1.2.1.25.4.2.1.2"
|
||||
local swrun_pid = "1.3.6.1.2.1.25.4.2.1.1"
|
||||
local swrun_path = "1.3.6.1.2.1.25.4.2.1.4"
|
||||
local swrun_params = "1.3.6.1.2.1.25.4.2.1.5"
|
||||
local new_tbl = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
|
||||
if ( v.oid:match("^" .. swrun_name) ) then
|
||||
local item = {}
|
||||
local objid = v.oid:gsub( "^" .. swrun_name, swrun_path)
|
||||
local value = get_value_from_table( tbl, objid )
|
||||
|
||||
if value and value:len() > 0 then
|
||||
table.insert( item, ("Path: %s"):format( value ) )
|
||||
end
|
||||
|
||||
objid = v.oid:gsub( "^" .. swrun_name, swrun_params)
|
||||
value = get_value_from_table( tbl, objid )
|
||||
|
||||
if value and value:len() > 0 then
|
||||
table.insert( item, ("Params: %s"):format( value ) )
|
||||
end
|
||||
|
||||
objid = v.oid:gsub( "^" .. swrun_name, swrun_pid)
|
||||
value = get_value_from_table( tbl, objid )
|
||||
|
||||
if value then
|
||||
table.insert( item, ("PID: %s"):format( value ) )
|
||||
end
|
||||
|
||||
item.name = v.value
|
||||
table.insert( item, value )
|
||||
table.insert( new_tbl, item )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return new_tbl
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local data, snmpoid = nil, "1.3.6.1.2.1.25.4.2"
|
||||
local shares = {}
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
shares = snmp_walk( socket, snmpoid )
|
||||
|
||||
if ( shares == nil ) or ( #shares == 0 ) then
|
||||
return
|
||||
end
|
||||
|
||||
shares = process_answer( shares )
|
||||
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
return stdnse.format_output( true, shares )
|
||||
end
|
||||
|
||||
121
scripts/snmp-win32-services.nse
Normal file
121
scripts/snmp-win32-services.nse
Normal file
@@ -0,0 +1,121 @@
|
||||
description = [[
|
||||
Attempts to enumerate Windows Services through SNMP
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- | snmp-win32-services:
|
||||
-- | Apache Tomcat
|
||||
-- | Application Experience Lookup Service
|
||||
-- | Application Layer Gateway Service
|
||||
-- | Automatic Updates
|
||||
-- | COM+ Event System
|
||||
-- | COM+ System Application
|
||||
-- | Computer Browser
|
||||
-- | Cryptographic Services
|
||||
-- | DB2 - DB2COPY1 - DB2
|
||||
-- | DB2 Management Service (DB2COPY1)
|
||||
-- | DB2 Remote Command Server (DB2COPY1)
|
||||
-- | DB2DAS - DB2DAS00
|
||||
-- |_ DCOM Server Process Launcher
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "discovery", "safe"}
|
||||
dependencies = {"snmp-brute"}
|
||||
|
||||
-- Version 0.2
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/19/2010 - v0.2 - fixed loop that would occure if a mib did not exist
|
||||
|
||||
require "shortport"
|
||||
require "snmp"
|
||||
|
||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
|
||||
--- Walks the MIB Tree
|
||||
--
|
||||
-- @param socket socket already connected to the server
|
||||
-- @base_oid string containing the base object ID to walk
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmp_walk( socket, base_oid )
|
||||
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
|
||||
while ( true ) do
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
|
||||
|
||||
try(socket:send(payload))
|
||||
response = try( socket:receive_bytes(1) )
|
||||
|
||||
snmpdata = snmp.fetchResponseValues( response )
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
end
|
||||
|
||||
socket:close()
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return snmp_table
|
||||
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
function process_answer( tbl )
|
||||
|
||||
local new_tab = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
table.insert( new_tab, v.value )
|
||||
end
|
||||
|
||||
table.sort( new_tab )
|
||||
|
||||
return new_tab
|
||||
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local snmpoid = "1.3.6.1.4.1.77.1.2.3.1.1"
|
||||
local services = {}
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
services = snmp_walk( socket, snmpoid )
|
||||
|
||||
if ( services == nil ) or ( #services == 0 ) then
|
||||
return
|
||||
end
|
||||
|
||||
services = process_answer(services)
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
return stdnse.format_output( true, services )
|
||||
end
|
||||
|
||||
142
scripts/snmp-win32-shares.nse
Normal file
142
scripts/snmp-win32-shares.nse
Normal file
@@ -0,0 +1,142 @@
|
||||
description = [[
|
||||
Attempts to enumerate Windows Shares through SNMP
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- | snmp-win32-shares:
|
||||
-- | SYSVOL
|
||||
-- | C:\WINDOWS\sysvol\sysvol
|
||||
-- | NETLOGON
|
||||
-- | C:\WINDOWS\sysvol\sysvol\inspectit-labb.local\SCRIPTS
|
||||
-- | Webapps
|
||||
-- |_ C:\Program Files\Apache Software Foundation\Tomcat 5.5\webapps\ROOT
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "discovery", "safe"}
|
||||
dependencies = {"snmp-brute"}
|
||||
|
||||
-- Version 0.2
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/19/2010 - v0.2 - fixed loop that would occure if a mib did not exist
|
||||
|
||||
require "shortport"
|
||||
require "snmp"
|
||||
|
||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
|
||||
--- Walks the MIB Tree
|
||||
--
|
||||
-- @param socket socket already connected to the server
|
||||
-- @base_oid string containing the base object ID to walk
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmp_walk( socket, base_oid )
|
||||
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
|
||||
while ( true ) do
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
|
||||
|
||||
try(socket:send(payload))
|
||||
response = try( socket:receive_bytes(1) )
|
||||
|
||||
snmpdata = snmp.fetchResponseValues( response )
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
end
|
||||
|
||||
socket:close()
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return snmp_table
|
||||
|
||||
end
|
||||
|
||||
--- Gets a value for the specified oid
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @param oid string containing the object id for which the value should be extracted
|
||||
-- @return value of relevant type or nil if oid was not found
|
||||
function get_value_from_table( tbl, oid )
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
if v.oid == oid then
|
||||
return v.value
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
function process_answer( tbl )
|
||||
|
||||
local share_name = "1.3.6.1.4.1.77.1.2.27.1.1"
|
||||
local share_path = "1.3.6.1.4.1.77.1.2.27.1.2"
|
||||
local new_tbl = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
|
||||
if ( v.oid:match("^" .. share_name) ) then
|
||||
local item = {}
|
||||
local objid = v.oid:gsub( "^" .. share_name, share_path)
|
||||
local path = get_value_from_table( tbl, objid )
|
||||
|
||||
item.name = v.value
|
||||
table.insert( item, path )
|
||||
table.insert( new_tbl, item )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return new_tbl
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local data, snmpoid = nil, "1.3.6.1.4.1.77.1.2.27"
|
||||
local shares = {}
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
shares = snmp_walk( socket, snmpoid )
|
||||
|
||||
if ( shares == nil ) or ( #shares == 0 ) then
|
||||
return
|
||||
end
|
||||
|
||||
shares = process_answer( shares )
|
||||
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
return stdnse.format_output( true, shares )
|
||||
end
|
||||
|
||||
147
scripts/snmp-win32-software.nse
Normal file
147
scripts/snmp-win32-software.nse
Normal file
@@ -0,0 +1,147 @@
|
||||
description = [[
|
||||
Attempts to enumerate installed software through SNMP
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- | snmp-win32-software:
|
||||
-- | Apache Tomcat 5.5 (remove only); 2007-09-15 15:13:18
|
||||
-- | Microsoft Internationalized Domain Names Mitigation APIs; 2007-09-15 15:13:18
|
||||
-- | Security Update for Windows Media Player (KB911564); 2007-09-15 15:13:18
|
||||
-- | Security Update for Windows Server 2003 (KB924667-v2); 2007-09-15 15:13:18
|
||||
-- | Security Update for Windows Media Player 6.4 (KB925398); 2007-09-15 15:13:18
|
||||
-- | Security Update for Windows Server 2003 (KB925902); 2007-09-15 15:13:18
|
||||
-- |_ Windows Internet Explorer 7; 2007-09-15 15:13:18
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "discovery", "safe"}
|
||||
dependencies = {"snmp-brute"}
|
||||
|
||||
-- Version 0.2
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/19/2010 - v0.2 - fixed loop that would occure if a mib did not exist
|
||||
|
||||
require "shortport"
|
||||
require "snmp"
|
||||
|
||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
|
||||
|
||||
--- Walks the MIB Tree
|
||||
--
|
||||
-- @param socket socket already connected to the server
|
||||
-- @base_oid string containing the base object ID to walk
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmp_walk( socket, base_oid )
|
||||
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
|
||||
while ( true ) do
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
|
||||
|
||||
try(socket:send(payload))
|
||||
response = try( socket:receive_bytes(1) )
|
||||
|
||||
snmpdata = snmp.fetchResponseValues( response )
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
end
|
||||
|
||||
socket:close()
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return snmp_table
|
||||
|
||||
end
|
||||
|
||||
--- Gets a value for the specified oid
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @param oid string containing the object id for which the value should be extracted
|
||||
-- @return value of relevant type or nil if oid was not found
|
||||
function get_value_from_table( tbl, oid )
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
if v.oid == oid then
|
||||
return v.value
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
function process_answer( tbl )
|
||||
|
||||
local sw_name = "1.3.6.1.2.1.25.6.3.1.2"
|
||||
local sw_date = "1.3.6.1.2.1.25.6.3.1.5"
|
||||
local new_tbl = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
|
||||
if ( v.oid:match("^" .. sw_name) ) then
|
||||
local objid = v.oid:gsub( "^" .. sw_name, sw_date)
|
||||
local install_date = get_value_from_table( tbl, objid )
|
||||
local sw_item
|
||||
|
||||
local _, year, month, day, hour, min, sec = bin.unpack( ">SCCCCC", install_date )
|
||||
install_date = ("%02d-%02d-%02d %02d:%02d:%02d"):format( year, month, day, hour, min, sec )
|
||||
|
||||
sw_item = ("%s; %s"):format(v.value ,install_date)
|
||||
table.insert( new_tbl, sw_item )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
table.sort( new_tbl )
|
||||
return new_tbl
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local data, snmpoid = nil, "1.3.6.1.2.1.25.6.3.1"
|
||||
local sw = {}
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
sw = snmp_walk( socket, snmpoid )
|
||||
|
||||
if ( sw == nil ) or ( #sw == 0 ) then
|
||||
return
|
||||
end
|
||||
|
||||
sw = process_answer( sw )
|
||||
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
return stdnse.format_output( true, sw )
|
||||
end
|
||||
|
||||
118
scripts/snmp-win32-users.nse
Normal file
118
scripts/snmp-win32-users.nse
Normal file
@@ -0,0 +1,118 @@
|
||||
description = [[
|
||||
Attempts to enumerate User Accounts through SNMP
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- | snmp-win32-users:
|
||||
-- | Administrator
|
||||
-- | Guest
|
||||
-- | IUSR_EDUSRV011
|
||||
-- | IWAM_EDUSRV011
|
||||
-- | SUPPORT_388945a0
|
||||
-- | Tomcat
|
||||
-- | db2admin
|
||||
-- | ldaptest
|
||||
-- |_ patrik
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default", "discovery", "safe"}
|
||||
dependencies = {"snmp-brute"}
|
||||
|
||||
-- Version 0.2
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/19/2010 - v0.2 - fixed loop that would occure if a mib did not exist
|
||||
|
||||
require "shortport"
|
||||
require "snmp"
|
||||
|
||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
|
||||
--- Walks the MIB Tree
|
||||
--
|
||||
-- @param socket socket already connected to the server
|
||||
-- @base_oid string containing the base object ID to walk
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmp_walk( socket, base_oid )
|
||||
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
|
||||
while ( true ) do
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode( snmp.buildPacket( snmp.buildGetNextRequest(options, oid) ) )
|
||||
|
||||
try(socket:send(payload))
|
||||
response = try( socket:receive_bytes(1) )
|
||||
|
||||
snmpdata = snmp.fetchResponseValues( response )
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
end
|
||||
|
||||
socket:close()
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return snmp_table
|
||||
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
--
|
||||
-- @param tbl table containing <code>oid</code> and <code>value</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
function process_answer( tbl )
|
||||
|
||||
local new_tab = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
table.insert( new_tab, v.value )
|
||||
end
|
||||
|
||||
table.sort( new_tab )
|
||||
|
||||
return new_tab
|
||||
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local snmpoid = "1.3.6.1.4.1.77.1.2.25"
|
||||
local users = {}
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
users = snmp_walk( socket, snmpoid )
|
||||
users = process_answer( users )
|
||||
|
||||
if ( users == nil ) or ( #users == 0 ) then
|
||||
return
|
||||
end
|
||||
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
return stdnse.format_output( true, users )
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user