mirror of
https://github.com/nmap/nmap.git
synced 2025-12-14 19:59:02 +00:00
o [NSE] Added RPC library and three new NFS scripts. Modified the rpcinfo and
nfs-showmount scripts to use the new library. The new scripts are: - nfs-acls shows the owner and directory mode of NFS exports - nfs-dirlist lists the contents of NFS exports - nfs-statfs shows file system statistics for NFS exports [Patrik]
This commit is contained in:
@@ -2,6 +2,13 @@
|
||||
|
||||
[NOT YET RELEASED]
|
||||
|
||||
o [NSE] Added RPC library and three new NFS scripts. Modified the rpcinfo and
|
||||
nfs-showmount scripts to use the new library. The new scripts are:
|
||||
- nfs-acls shows the owner and directory mode of NFS exports
|
||||
- nfs-dirlist lists the contents of NFS exports
|
||||
- nfs-statfs shows file system statistics for NFS exports
|
||||
[Patrik]
|
||||
|
||||
o Fixed the Idle Scan (-sI) so that scanning multiple hosts doesn't
|
||||
retest the zombie proxy and reinitialize all of the associated data
|
||||
at the beginning of each run. [Kris]
|
||||
|
||||
1681
nselib/rpc.lua
Normal file
1681
nselib/rpc.lua
Normal file
File diff suppressed because it is too large
Load Diff
65
scripts/nfs-acls.nse
Normal file
65
scripts/nfs-acls.nse
Normal file
@@ -0,0 +1,65 @@
|
||||
description = [[
|
||||
Shows NFS exports and access controls.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 111/tcp open rpcbind
|
||||
-- | nfs-acls:
|
||||
-- | /tmp
|
||||
-- | uid: 0; gid: 0; mode: drwxrwxrwx (1777)
|
||||
-- | /home/storage/backup
|
||||
-- | uid: 0; gid: 0; mode: drwxr-xr-x (755)
|
||||
-- | /home
|
||||
-- |_ uid: 0; gid: 0; mode: drwxr-xr-x (755)
|
||||
--
|
||||
|
||||
-- Version 0.6
|
||||
|
||||
-- Created 11/23/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 11/24/2009 - v0.2 - added RPC query to find mountd ports
|
||||
-- Revised 11/24/2009 - v0.3 - added a hostrule instead of portrule
|
||||
-- Revised 11/26/2009 - v0.4 - reduced packet sizes and documented them
|
||||
-- Revised 01/24/2009 - v0.5 - complete rewrite, moved all NFS related code into nselib/nfs.lua
|
||||
-- Revised 02/22/2009 - v0.6 - adapted to support new RPC library
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require("shortport")
|
||||
require("rpc")
|
||||
|
||||
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, mounts, attribs
|
||||
local result = {}
|
||||
|
||||
status, mounts = rpc.Helper.ShowMounts( host, port )
|
||||
|
||||
if ( not(status) or mounts == nil ) then
|
||||
return " \n\n Failed to list mount points"
|
||||
end
|
||||
|
||||
for _, mount in ipairs( mounts ) do
|
||||
local item = {}
|
||||
status, attribs = rpc.Helper.GetAttributes( host, port, mount.name )
|
||||
|
||||
item.name = mount.name
|
||||
|
||||
if ( status ) then
|
||||
table.insert(item, ("uid: %d; gid: %d; mode: %s (%d)"):format(attribs.uid, attribs.gid, rpc.Util.ToAclText( attribs.mode ), rpc.Util.ToAclMode( attribs.mode )) )
|
||||
else
|
||||
table.insert(item, "ERROR: Mount failed")
|
||||
end
|
||||
|
||||
table.insert(result, item)
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, result )
|
||||
|
||||
end
|
||||
86
scripts/nfs-dirlist.nse
Normal file
86
scripts/nfs-dirlist.nse
Normal file
@@ -0,0 +1,86 @@
|
||||
description = [[
|
||||
Does a directory listing of a remote NFS share
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 111/tcp open rpcbind
|
||||
-- | nfs-dirlist:
|
||||
-- | /home/storage/backup
|
||||
-- | www.cqure.net
|
||||
-- | /home
|
||||
-- | admin
|
||||
-- | lost+found
|
||||
-- | patrik
|
||||
-- | storage
|
||||
-- |_ web
|
||||
--
|
||||
|
||||
-- Version 0.3
|
||||
--
|
||||
-- Created 01/25/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 02/22/2010 - v0.2 - adapted to support new RPC library
|
||||
-- Revised 03/13/2010 - v0.3 - converted host to port rule
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require("shortport")
|
||||
require("rpc")
|
||||
|
||||
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, mounts
|
||||
local result, files = {}, {}
|
||||
local hasmore = false
|
||||
local proto
|
||||
|
||||
status, mounts = rpc.Helper.ShowMounts( host, port )
|
||||
|
||||
if ( not(status) ) then
|
||||
return " \n\n Failed to list mount points"
|
||||
end
|
||||
|
||||
for _, v in ipairs( mounts ) do
|
||||
local files = {}
|
||||
local status, dirlist = rpc.Helper.Dir(host, port, v.name)
|
||||
|
||||
if status and dirlist then
|
||||
local max_files = tonumber(nmap.registry.args.nfs_max_files) or 10
|
||||
hasmore = false
|
||||
for _, v in ipairs( dirlist.entries ) do
|
||||
if #files >= max_files then
|
||||
hasmore = true
|
||||
break
|
||||
end
|
||||
|
||||
if v.name ~= ".." and v.name ~= "." then
|
||||
table.insert(files, v.name)
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(files)
|
||||
|
||||
if hasmore then
|
||||
files.name = v.name .. string.format(" (Output limited to %d files)", max_files )
|
||||
else
|
||||
files.name = v.name
|
||||
end
|
||||
|
||||
table.insert( result, files )
|
||||
else
|
||||
files.name = v.name
|
||||
table.insert(files, "ERROR: Mount failed")
|
||||
table.insert( result, files )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, result )
|
||||
|
||||
end
|
||||
@@ -6,334 +6,48 @@ Shows NFS exports, like the <code>showmount -e</code> command.
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 111/tcp open rpcbind
|
||||
--
|
||||
-- Host script results:
|
||||
-- | nfs-showmount:
|
||||
-- | /home/storage/backup 10.46.200.0/255.255.255.0 10.46.200.66/255.255.255.255
|
||||
-- |_ /home 10.46.200.0/255.255.255.0
|
||||
-- | /home/storage/backup 10.46.200.0/255.255.255.0
|
||||
-- |_ /home 1.2.3.4/255.255.255.255 10.46.200.0/255.255.255.0
|
||||
--
|
||||
|
||||
-- Version 0.4
|
||||
-- Version 0.7
|
||||
|
||||
-- Created 11/23/2009 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 11/24/2009 - v0.2 - added RPC query to find mountd ports
|
||||
-- Revised 11/24/2009 - v0.3 - added a hostrule instead of portrule
|
||||
-- Revised 11/26/2009 - v0.4 - reduced packet sizes and documented them
|
||||
-- Revised 01/24/2009 - v0.5 - complete rewrite, moved all NFS related code into nselib/nfs.lua
|
||||
-- Revised 02/22/2009 - v0.6 - adapted to support new RPC library
|
||||
-- Revised 03/13/2010 - v0.7 - converted host to port rule
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require 'comm'
|
||||
require 'datafiles'
|
||||
require("shortport")
|
||||
require("rpc")
|
||||
|
||||
hostrule = function(host)
|
||||
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
|
||||
|
||||
local port_t111 = nmap.get_port_state(host, {number=111, protocol="udp"})
|
||||
local port_u111 = nmap.get_port_state(host, {number=111, protocol="tcp"})
|
||||
action = function(host, port)
|
||||
|
||||
return ( port_t111 ~= nil and port_t111.state == "open") or
|
||||
(port_u111 ~= nil and (port_u111.state == "open" or
|
||||
port_u111.state == "open|filtered"))
|
||||
local status, mounts, proto
|
||||
local result = {}
|
||||
|
||||
status, mounts = rpc.Helper.ShowMounts( host, port )
|
||||
|
||||
if not status or mounts == nil then
|
||||
return " \n\n Failed to list mount points"
|
||||
end
|
||||
|
||||
--
|
||||
-- Calculates the number of fill bytes needed
|
||||
-- @param length contains the length of the string
|
||||
-- @return the amount of pad needed to be divideable by 4
|
||||
--
|
||||
function calc_fill_bytes(length)
|
||||
|
||||
-- calculate fill bytes
|
||||
if math.mod( length, 4 ) ~= 0 then
|
||||
return (4 - math.mod( length, 4))
|
||||
else
|
||||
return 0
|
||||
for _, v in ipairs( mounts ) do
|
||||
local entry = v.name
|
||||
entry = entry .. " " .. stdnse.strjoin(" ", v)
|
||||
table.insert( result, entry )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- extracts the group from the export list entry
|
||||
-- @param data string should be start with 32-bit lenght field
|
||||
--
|
||||
-- @return pos numeric new position within buffer, grp_val string the group contents
|
||||
--
|
||||
function extract_group(data)
|
||||
|
||||
local pos, grp_val
|
||||
|
||||
-- retrieve the group length
|
||||
pos, grp_len = bin.unpack( ">i", data )
|
||||
data = data:sub(pos)
|
||||
|
||||
-- retrieve the group contents
|
||||
grp_val = data:sub(0, grp_len)
|
||||
pos = 4 + calc_fill_bytes(grp_len) + grp_len + 1
|
||||
|
||||
return pos, grp_val
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- extracts the directory from the export list entry
|
||||
-- @param data string should be start with 32-bit lenght field
|
||||
--
|
||||
-- @return pos numeric new position within buffer, dir_name string the name of the directory
|
||||
--
|
||||
function extract_directory(data)
|
||||
|
||||
local pos, dir_len, dir_name
|
||||
|
||||
-- retrieve the length of the directory name
|
||||
pos, dir_len = bin.unpack(">i", data)
|
||||
data = data:sub(pos)
|
||||
|
||||
-- retrieve the directory name
|
||||
dir_name = data:sub(0, dir_len)
|
||||
pos = 4 + calc_fill_bytes(dir_len) + dir_len + 1
|
||||
|
||||
return pos, dir_name
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- processes the response back from the mountd service
|
||||
-- @param proto string should be either "udp" or "tcp"
|
||||
-- @param data string contains the response recieved from the service
|
||||
--
|
||||
-- @return string with exports from NFS
|
||||
--
|
||||
function process_response(proto, data)
|
||||
|
||||
local pos, val_follows
|
||||
local header = {}
|
||||
local response=" \n"
|
||||
|
||||
-- if we're running over UDP skip first 4 bytes ( theres no 16-bit something + 16-bit length)
|
||||
if "udp" == proto then
|
||||
pos, header['xid'], header['type'], header['state'],
|
||||
header['verifier'], header['accept_state'] = bin.unpack(">iiili", data)
|
||||
else
|
||||
pos, _, header['length'], header['xid'], header['type'], header['state'],
|
||||
header['verifier'], header['accept_state'] = bin.unpack("S>Siiili", data)
|
||||
end
|
||||
|
||||
data = data:sub(pos)
|
||||
|
||||
-- We should probably be doing a lot more verification here, but let's stick to basics
|
||||
-- Was the response from the server = Reply(1) and
|
||||
-- Accept state = RPC Executed succefully (0)
|
||||
if header['type'] ~= 1 or header['accept_state'] ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Each export list entry consists of:
|
||||
--
|
||||
-- One or more Directory entries:
|
||||
-- 32-bit - length
|
||||
-- length - directory name
|
||||
--
|
||||
-- One or more Group entries:
|
||||
-- 32-bit - length
|
||||
-- length - group contents
|
||||
--
|
||||
-- Every group entry is separated by
|
||||
-- 32-bit - value follows - if set to 1 more groups exist
|
||||
--
|
||||
-- Every directory entry is separated by
|
||||
-- 32-bit - value follows - if set to 1 more entries exist
|
||||
--
|
||||
--
|
||||
-- Note: The length specifies the amount of characters for
|
||||
-- both dir and group entries
|
||||
--
|
||||
-- However, directories and groups are padded by zeroes so that
|
||||
-- they are divideable by 4. Hence calc_fill_bytes
|
||||
--
|
||||
|
||||
pos, val_follows = bin.unpack(">i", data)
|
||||
data = data:sub(pos)
|
||||
|
||||
while 1 == val_follows do
|
||||
|
||||
local dir_name, exp_group
|
||||
local grp_follows, grp_len, grp_val
|
||||
|
||||
groups=""
|
||||
|
||||
pos, dir_name = extract_directory( data )
|
||||
data = data:sub(pos)
|
||||
|
||||
-- check if we have a group following
|
||||
pos, grp_follows = bin.unpack(">i", data )
|
||||
data = data:sub(pos)
|
||||
|
||||
while grp_follows == 1 do
|
||||
|
||||
pos, grp_val = extract_group( data )
|
||||
groups = groups .. " " .. grp_val
|
||||
|
||||
data = data:sub(pos)
|
||||
|
||||
-- check if there's antoher group following
|
||||
pos, grp_follows = bin.unpack(">i", data )
|
||||
data = data:sub(pos)
|
||||
|
||||
end
|
||||
|
||||
-- concatenate our dir_name and groups to the result
|
||||
response = response .. dir_name .. "" .. groups .. "\n"
|
||||
|
||||
-- are there any more directory entries?
|
||||
pos, val_follows = bin.unpack(">i", data)
|
||||
data = data:sub(pos)
|
||||
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
--
|
||||
-- Ruthlessly ripped, and modified, from Sven Klemm's rpcinfo.nse script
|
||||
--
|
||||
function get_rpc_port_for_service(host, svc_progname, svc_version)
|
||||
|
||||
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(socket:connect(host.ip, 111))
|
||||
|
||||
-- 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(socket:receive_bytes(1))
|
||||
|
||||
local _,offset,header,length,tx_id,msg_type,reply_state,accept_state,value,payload,last_fragment
|
||||
last_fragment = false; offset = 1; payload = ''
|
||||
|
||||
-- extract payload from answer and try to receive more packets if
|
||||
-- RPC header with last_fragment set has not been received
|
||||
-- If we can't get further packets don't stop but process what we
|
||||
-- got so far.
|
||||
while not last_fragment do
|
||||
if offset > #answer then
|
||||
local status, data = socket:receive_bytes(1)
|
||||
if not status then break end
|
||||
answer = answer .. data
|
||||
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 prog, version, proto, port
|
||||
local ports = {}
|
||||
offset, value = bin.unpack('>I',payload,offset)
|
||||
while value == 1 and #payload - offset >= 19 do
|
||||
offset,prog,version,proto,port,value = bin.unpack('>IIIII',payload,offset)
|
||||
proto = protocols[proto] or tostring( proto )
|
||||
|
||||
if rpc_numbers[prog] == svc_progname and version == svc_version then
|
||||
ports[proto] = port
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return ports
|
||||
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local data = {}
|
||||
|
||||
-- packet copy/pasted from wireshark, running showmount -e
|
||||
|
||||
data["tcp"] = string.char(
|
||||
0x80, 0x00, 0x00, 0x28, -- Fragment Length: 44 bytes (31-bit?)
|
||||
-- Last Fragment: Yes
|
||||
|
||||
0x21, 0x00, 0x46, 0x4c, -- XID: 0x2100464c
|
||||
0x00, 0x00, 0x00, 0x00, -- Message type: Call(0)
|
||||
0x00, 0x00, 0x00, 0x02, -- RPC Version: 2
|
||||
0x00, 0x01, 0x86, 0xa5, -- Program: MOUNT(100005)
|
||||
0x00, 0x00, 0x00, 0x01, -- Program Version: 1
|
||||
0x00, 0x00, 0x00, 0x05, -- Procedure: EXPORT(5)
|
||||
|
||||
-- Credentials
|
||||
0x00, 0x00, 0x00, 0x00, -- Flavor: AUTH_NULL (0)
|
||||
0x00, 0x00, 0x00, 0x00, -- Length: 0
|
||||
|
||||
-- Verifier
|
||||
0x00, 0x00, 0x00, 0x00, -- Flavor: AUTH_NULL
|
||||
0x00, 0x00, 0x00, 0x00 -- Length: 0
|
||||
)
|
||||
|
||||
data["udp"] = string.char(
|
||||
0x21, 0x00, 0x46, 0x4c, -- XID: 0x2100464c
|
||||
0x00, 0x00, 0x00, 0x00, -- Message type: Call(0)
|
||||
0x00, 0x00, 0x00, 0x02, -- RPC Version: 2
|
||||
0x00, 0x01, 0x86, 0xa5, -- Program: MOUNT(100005)
|
||||
0x00, 0x00, 0x00, 0x01, -- Program Version: 1
|
||||
0x00, 0x00, 0x00, 0x05, -- Procedure: EXPORT(5)
|
||||
|
||||
-- Credentials
|
||||
0x00, 0x00, 0x00, 0x00, -- Flavor: AUTH_NULL (0)
|
||||
0x00, 0x00, 0x00, 0x00, -- Length: 0
|
||||
|
||||
-- Verifier
|
||||
0x00, 0x00, 0x00, 0x00, -- Flavor: AUTH_NULL
|
||||
0x00, 0x00, 0x00, 0x00 -- Length: 0
|
||||
|
||||
)
|
||||
|
||||
|
||||
local status, result
|
||||
local ports = get_rpc_port_for_service(host, "mountd", 1)
|
||||
|
||||
for p in pairs(ports) do
|
||||
|
||||
status, result = comm.exchange(host, ports[p], data[p], {proto=p})
|
||||
|
||||
-- Fail gracefully
|
||||
if not status then
|
||||
if (nmap.verbosity() >= 2 or nmap.debugging() >= 1) then
|
||||
return "ERROR: TIMEOUT"
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
result = process_response( p, result )
|
||||
|
||||
if result then
|
||||
return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return
|
||||
return stdnse.format_output( true, result )
|
||||
|
||||
end
|
||||
|
||||
69
scripts/nfs-statfs.nse
Normal file
69
scripts/nfs-statfs.nse
Normal file
@@ -0,0 +1,69 @@
|
||||
description = [[
|
||||
Retrieves disk space statistics from the remote NFS share
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- | nfs-statfs:
|
||||
-- | /home/storage/backup
|
||||
-- | Block size: 512
|
||||
-- | Total blocks: 1901338728
|
||||
-- | Free blocks: 729769328
|
||||
-- | Available blocks: 633186880
|
||||
-- | /home
|
||||
-- | Block size: 512
|
||||
-- | Total blocks: 1901338728
|
||||
-- | Free blocks: 729769328
|
||||
-- |_ Available blocks: 633186880
|
||||
--
|
||||
|
||||
-- Version 0.3
|
||||
|
||||
-- Created 01/25/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 02/22/2010 - v0.2 - adapted to support new RPC library
|
||||
-- Revised 03/13/2010 - v0.3 - converted host to port rule
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require("shortport")
|
||||
require("rpc")
|
||||
|
||||
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local result, entry = {}, {}
|
||||
local status, mounts = rpc.Helper.ShowMounts( host, port )
|
||||
|
||||
if ( not(status) ) then
|
||||
return " \n\n Failed to list mount points"
|
||||
end
|
||||
|
||||
for _, v in ipairs( mounts ) do
|
||||
local entry = {}
|
||||
local status, stats = rpc.Helper.ExportStats(host, port, v.name)
|
||||
|
||||
if ( not(status) and stats:match("Version %d not supported") ) then
|
||||
return " \n\n " .. stats
|
||||
end
|
||||
|
||||
entry.name = v.name
|
||||
|
||||
if status and stats then
|
||||
table.insert( entry, string.format("Block size: %d", stats.block_size) )
|
||||
table.insert( entry, string.format("Total blocks: %d", stats.total_blocks) )
|
||||
table.insert( entry, string.format("Free blocks: %d", stats.free_blocks) )
|
||||
table.insert( entry, string.format("Available blocks: %d", stats.available_blocks) )
|
||||
else
|
||||
table.insert( entry, "ERROR: Mount failed")
|
||||
end
|
||||
table.insert( result, entry )
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, result )
|
||||
|
||||
end
|
||||
@@ -4,122 +4,43 @@ Connects to portmapper and fetches a list of all registered programs.
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- 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
|
||||
-- | 100000 2 111/udp rpcbind
|
||||
-- | 100003 2 2049/tcp nfs
|
||||
-- | 100003 2 2049/udp nfs
|
||||
-- | 100005 1,2 953/udp mountd
|
||||
-- | 100005 1,2 956/tcp mountd
|
||||
-- | 100024 1 55145/tcp status
|
||||
-- |_ 100024 1 59421/udp status
|
||||
|
||||
|
||||
require "shortport"
|
||||
require "datafiles"
|
||||
require "bin"
|
||||
require "bit"
|
||||
require "tab"
|
||||
|
||||
author = "Sven Klemm"
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"default","safe","discovery"}
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
portrule = shortport.port_or_service(111, "rpcbind")
|
||||
require 'shortport'
|
||||
require 'rpc'
|
||||
|
||||
--- 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
|
||||
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
|
||||
|
||||
action = function(host, port)
|
||||
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(socket:connect(host.ip, port.number))
|
||||
local result = {}
|
||||
local status, rpcinfo = rpc.Helper.RpcInfo( host, port )
|
||||
|
||||
-- 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 status, answer = socket:receive_bytes(1)
|
||||
if not status then
|
||||
stdnse.print_debug(1, "%s failed to receive a response from %s:%d with error: %s",
|
||||
filename:match( "[\\/]([^\\/]+)\.nse$" ) or filename,
|
||||
host.ip, port.number,
|
||||
answer or "unknown")
|
||||
socket:close()
|
||||
return nil
|
||||
if ( not(status) ) then
|
||||
return
|
||||
end
|
||||
|
||||
local _,offset,header,length,tx_id,msg_type,reply_state,accept_state,value,payload,last_fragment
|
||||
last_fragment = false; offset = 1; payload = ''
|
||||
|
||||
-- extract payload from answer and try to receive more packets if
|
||||
-- RPC header with last_fragment set has not been received
|
||||
-- If we can't get further packets don't stop but process what we
|
||||
-- got so far.
|
||||
while not last_fragment do
|
||||
if offset > #answer then
|
||||
local status, data = socket:receive_bytes(1)
|
||||
if not status then break end
|
||||
answer = answer .. data
|
||||
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 prog, version, proto, port
|
||||
offset, value = bin.unpack('>I',payload,offset)
|
||||
while value == 1 and #payload - offset >= 19 do
|
||||
offset,prog,version,proto,port,value = bin.unpack('>IIIII',payload,offset)
|
||||
proto = protocols[proto] or tostring( proto )
|
||||
-- collect data in a table
|
||||
dir[proto] = dir[proto] or {}
|
||||
dir[proto][port] = dir[proto][port] or {}
|
||||
dir[proto][port][prog] = dir[proto][port][prog] or {}
|
||||
table.insert( dir[proto][port][prog], version )
|
||||
end
|
||||
|
||||
-- format output
|
||||
local output = tab.new(4)
|
||||
for proto, o in pairs(dir) do
|
||||
-- get list of all used ports
|
||||
local ports = {}
|
||||
for port,_ in pairs(o) do table.insert(ports, port) end
|
||||
table.sort(ports)
|
||||
|
||||
-- iterate over ports to produce output
|
||||
for _, port in ipairs(ports) do
|
||||
for prog, versions in pairs(o[port]) do
|
||||
local name = rpc_numbers[prog] or ''
|
||||
tab.addrow(output,prog,format_version(versions),('%5d/%s'):format(port,proto),name)
|
||||
for progid, v in pairs(rpcinfo) do
|
||||
for proto, v2 in pairs(v) do
|
||||
table.insert( result, ("%-7d %-10s %5d/%s %s"):format(progid, stdnse.strjoin(",", v2.version), v2.port, proto, rpc.Util.ProgNumberToName(progid) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
return ' \n' .. tab.dump( output )
|
||||
|
||||
table.sort(result)
|
||||
return stdnse.format_output( true, result )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
Entry { filename = "afp-path-exploit.nse", categories = { "safe", "vuln", } }
|
||||
Entry { filename = "afp-path-vuln.nse", categories = { "safe", "vuln", } }
|
||||
Entry { filename = "afp-showmount.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "asn-query.nse", categories = { "discovery", "external", "safe", } }
|
||||
Entry { filename = "auth-owners.nse", categories = { "default", "safe", } }
|
||||
@@ -57,7 +59,10 @@ Entry { filename = "mysql-info.nse", categories = { "default", "discovery", "saf
|
||||
Entry { filename = "mysql-users.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "mysql-variables.nse", categories = { "discovery", "intrusive", } }
|
||||
Entry { filename = "nbstat.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "nfs-acls.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "nfs-dirlist.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "nfs-showmount.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "nfs-statfs.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "ntp-info.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "oracle-sid-brute.nse", categories = { "auth", "intrusive", } }
|
||||
Entry { filename = "p2p-conficker.nse", categories = { "default", "safe", } }
|
||||
@@ -68,7 +73,7 @@ Entry { filename = "pop3-capabilities.nse", categories = { "default", "discovery
|
||||
Entry { filename = "pptp-version.nse", categories = { "version", } }
|
||||
Entry { filename = "realvnc-auth-bypass.nse", categories = { "default", "safe", "vuln", } }
|
||||
Entry { filename = "robots.txt.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "rpcinfo.nse", categories = { "default", "discovery", "safe", } }
|
||||
Entry { filename = "rpcinfo.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "skypev2-version.nse", categories = { "version", } }
|
||||
Entry { filename = "smb-brute.nse", categories = { "auth", "intrusive", } }
|
||||
Entry { filename = "smb-check-vulns.nse", categories = { "dos", "exploit", "intrusive", "vuln", } }
|
||||
|
||||
Reference in New Issue
Block a user