1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-20 13:19:01 +00:00

o Updated rpcinfo NSE script to use the new pack/unpack (binlib)

functions, use the new tab library, include better documentation, and
  fix some bugs. [Sven Klemm]
This commit is contained in:
fyodor
2008-09-03 08:31:05 +00:00
parent f67049b2d3
commit bed9523832
2 changed files with 83 additions and 59 deletions

View File

@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*-
o Updated rpcinfo NSE script to use the new pack/unpack (binlib)
functions, use the new tab library, include better documentation, and
fix some bugs. [Sven Klemm]
o Fix a bug in the NSE http library which would cause some scripts to
give the error: SCRIPT ENGINE: C:\Program
Files\Nmap\nselib/http.lua:77: attempt to call field 'parse' (a nil
@@ -21,7 +25,7 @@ o Added new addrow() function to NSE tab library. It allows
o The NSE http library now supports chunked encoding. [Sven Klemm]
o Fix a number of NSE scripts which used print_debug()
o Fixed a number of NSE scripts which used print_debug()
incorrectly. See
http://seclists.org/nmap-dev/2008/q3/0470.html. [Sven Klemm].

View File

@@ -1,94 +1,114 @@
id = "rpcinfo"
---
-- Connects to portmapper and fetches a list of all registered programs
--
--@output
-- 111/tcp open rpcbind
-- | rpcinfo:
-- | 100000 2 111/udp rpcbind
-- | 100005 1,2,3 705/udp mountd
-- | 100003 2,3,4 2049/udp nfs
-- | 100024 1 32769/udp status
-- | 100021 1,3,4 32769/udp nlockmgr
-- | 100000 2 111/tcp rpcbind
-- | 100005 1,2,3 706/tcp mountd
-- | 100003 2,3,4 2049/tcp nfs
-- | 100024 1 50468/tcp status
-- |_ 100021 1,3,4 50468/tcp nlockmgr
description = "connects to portmapper and prints a list of all registered programs"
require "shortport"
require "datafiles"
require "bin"
require "bit"
require "tab"
id = "rpcinfo"
description = "connects to portmapper and fetches a list of all registered programs"
author = "Sven Klemm <sven@c3d2.de>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default","safe","discovery"}
require "comm"
require "shortport"
require "packet"
require "datafiles"
portrule = shortport.port_or_service(111, "rpcbind")
--- format a table of version for output
--@param version_table table containing the versions
--@return string with the formatted versions
local format_version = function( version_table )
table.sort( version_table )
return table.concat( version_table, ',' )
end
action = function(host, port)
local try
local transaction_id = "nmap"
local result = " \n"
local rpc_numbers
local socket = nmap.new_socket()
socket:set_timeout(1000)
local catch = function() socket:close() end
local try = nmap.new_try(catch)
local rpc_numbers = try(datafiles.parse_rpc())
try = nmap.new_try()
rpc_numbers = try(datafiles.parse_rpc())
try(socket:connect(host.ip, port.number))
local request = string.char(0x80,0,0,40) -- fragment header
request = request .. transaction_id -- transaction id
request = request .. "\0\0\0\0\0\0\0\2" -- message type: call (0) and rpc version 2
request = request .. string.char(0,1,134,160) -- programm portmap (100000)
request = request .. "\0\0\0\2\0\0\0\4" -- programm version (2) procedure dump(4)
request = request .. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"-- Credentials and verifier
-- build rpc dump call packet
local transaction_id = math.random(0x7FFFFFFF)
local request = bin.pack('>IIIIIIILL',0x80000028,transaction_id,0,2,100000,2,4,0,0)
try(socket:send(request))
local answer = try(comm.exchange(host, port, request, {timeout=1000}))
local answer_part
local answer = try(socket:receive_bytes(1))
local fragment_length = answer:byte(4) + answer:byte(3) * 256 + answer:byte(2) * 65536
if answer:sub(5,8) == transaction_id and answer:byte(12) == 1 and answer:byte(16) == 0 and answer:byte(28) == 0 then
-- transaction_id matches, message type reply, reply state accepted and accept state executed successfully
answer_part = answer
answer = answer_part:sub( 28 + 1, fragment_length + 4 )
answer_part = answer_part:sub( fragment_length + 4 + 1 )
local _,offset,header,length,tx_id,msg_type,reply_state,accept_state,value,payload,last_fragment
last_fragment = false; offset = 1; payload = ''
while answer_part:len() > 0 do -- defragment packet
fragment_length = answer_part:byte(4) + answer_part:byte(3) * 256 + answer_part:byte(2) * 65536
answer = answer .. answer_part:sub( 5, fragment_length + 4 )
answer_part = answer_part:sub( fragment_length + 4 + 1 )
-- extract payload from answer and try to receive more packets if header with
-- last_fragment set has not been received
while not last_fragment do
if offset > #answer then
answer = answer .. try(socket:receive_bytes(1))
end
offset,header = bin.unpack('>I',answer,offset)
last_fragment = bit.band( header, 0x80000000 ) ~= 0
length = bit.band( header, 0x7FFFFFFF )
payload = payload .. answer:sub( offset, offset + length - 1 )
offset = offset + length
end
socket:close()
offset,tx_id,msg_type,reply_state,_,_,accept_state = bin.unpack( '>IIIIII', payload )
-- transaction_id matches, message type reply, reply state accepted and accept state executed successfully
if tx_id == transaction_id and msg_type == 1 and reply_state == 0 and accept_state == 0 then
local dir = { udp = {}, tcp = {}}
local protocols = {[6]='tcp',[17]='udp'}
local rpc_prog, rpc_vers, rpc_proto, rpc_port
while answer:byte(4) == 1 and answer:len() >= 20 do
rpc_prog = packet.u32( answer, 4 )
rpc_vers = packet.u32( answer, 8 )
rpc_proto = packet.u32( answer, 12 )
rpc_port = packet.u32( answer, 16 )
answer = answer:sub(21)
if rpc_proto == 6 then
rpc_proto = "tcp"
elseif rpc_proto == 17 then
rpc_proto = "udp"
end
if not dir[rpc_proto][rpc_port] then dir[rpc_proto][rpc_port] = {} end
if not dir[rpc_proto][rpc_port][rpc_prog] then dir[rpc_proto][rpc_port][rpc_prog] = {} end
offset, value = bin.unpack('>I',payload,offset)
while value == 1 and #payload - offset >= 19 do
offset,rpc_prog,rpc_vers,rpc_proto,rpc_port,value = bin.unpack('>IIIII',payload,offset)
rpc_proto = protocols[rpc_proto] or tostring( rpc_proto )
-- collect data in a table
dir[rpc_proto] = dir[rpc_proto] or {}
dir[rpc_proto][rpc_port] = dir[rpc_proto][rpc_port] or {}
dir[rpc_proto][rpc_port][rpc_prog] = dir[rpc_proto][rpc_port][rpc_prog] or {}
table.insert( dir[rpc_proto][rpc_port][rpc_prog], rpc_vers )
end
local format_version = function( version_table )
if #version_table == 1 then return version_table[1] end
table.sort( version_table )
for i=2,#version_table do
if version_table[i-1] ~= version_table[i] - 1 then
return table.concat( version_table, ',' )
end
end
return string.format('%d-%d',version_table[1],version_table[#version_table])
end
-- format output
local output = tab.new(4)
for rpc_proto, o in pairs(dir) do
-- get list of all used ports
local ports = {}
for rpc_port, i in pairs(o) do table.insert(ports, rpc_port) end
table.sort(ports)
-- iterate over ports to produce output
for i, rpc_port in ipairs(ports) do
i = o[rpc_port]
for rpc_prog, versions in pairs(o[rpc_port]) do
versions = format_version( versions )
local name = rpc_numbers[rpc_prog] or ''
result = result .. string.format('%d %-5s %5d/%s %s\n',rpc_prog,versions,rpc_port,rpc_proto,name)
tab.addrow(output,rpc_prog,format_version(versions),('%5d/%s'):format(rpc_port,rpc_proto),name)
end
end
end
return ' \n' .. tab.dump( output )
end
return result
end