mirror of
https://github.com/nmap/nmap.git
synced 2025-12-12 02:39:03 +00:00
Add the nfs-showmount script by Patrik Karlsson.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o Added a new script, nfs-showmount by Patrik Karlsson, that shows nfs
|
||||||
|
exports like "showmount -e".
|
||||||
|
|
||||||
o Added a UDP SIPOptions probe corresponding to the TCP one thanks to
|
o Added a UDP SIPOptions probe corresponding to the TCP one thanks to
|
||||||
the research and testing of Patrik Karlsson and Matt Selsky.
|
the research and testing of Patrik Karlsson and Matt Selsky.
|
||||||
|
|
||||||
|
|||||||
339
scripts/nfs-showmount.nse
Normal file
339
scripts/nfs-showmount.nse
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
description = [[
|
||||||
|
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
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Version 0.4
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery", "safe"}
|
||||||
|
|
||||||
|
require 'comm'
|
||||||
|
require 'datafiles'
|
||||||
|
|
||||||
|
hostrule = function(host)
|
||||||
|
|
||||||
|
local port_t111 = nmap.get_port_state(host, {number=111, protocol="udp"})
|
||||||
|
local port_u111 = nmap.get_port_state(host, {number=111, protocol="tcp"})
|
||||||
|
|
||||||
|
return ( port_t111 ~= nil and port_t111.state == "open") or
|
||||||
|
(port_u111 ~= nil and (port_u111.state == "open" or
|
||||||
|
port_u111.state == "open|filtered"))
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
end
|
||||||
@@ -31,6 +31,7 @@ Entry { filename = "irc-info.nse", categories = { "default", "discovery", "safe"
|
|||||||
Entry { filename = "ms-sql-info.nse", categories = { "default", "discovery", "intrusive", } }
|
Entry { filename = "ms-sql-info.nse", categories = { "default", "discovery", "intrusive", } }
|
||||||
Entry { filename = "mysql-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "mysql-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "nbstat.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "nbstat.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
Entry { filename = "nfs-showmount.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "p2p-conficker.nse", categories = { "default", "safe", } }
|
Entry { filename = "p2p-conficker.nse", categories = { "default", "safe", } }
|
||||||
Entry { filename = "pjl-ready-message.nse", categories = { "intrusive", } }
|
Entry { filename = "pjl-ready-message.nse", categories = { "intrusive", } }
|
||||||
Entry { filename = "pop3-brute.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "pop3-brute.nse", categories = { "auth", "intrusive", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user