diff --git a/CHANGELOG b/CHANGELOG
index d1fe6de4c..e7e97e2de 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Added AUTH_UNIX support to the rpc library and nfs scripts.
+ [Daniel Miller]
+
o [Zenmap] Fixed a crash in the profile editor that would happen when
the nmap binary couldn't be found. [David Fifield]
diff --git a/nselib/rpc.lua b/nselib/rpc.lua
index 5d3ab40fb..932b5232b 100644
--- a/nselib/rpc.lua
+++ b/nselib/rpc.lua
@@ -164,9 +164,25 @@ Comm = {
end
if ( port.protocol == "tcp" ) then
socket = nmap.new_socket()
- status, err = socket:connect(host, port)
+ if nmap.is_privileged() then
+ -- Try to bind to a reserved port
+ for resvport = 600, 1024, 1 do
+ status, err = socket:bind(nil, 1000)
+ if status then
+ status, err = socket:connect(host, port)
+ if status then break end
+ end
+ end
+ end
else
socket = nmap.new_socket("udp")
+ if nmap.is_privileged() then
+ -- Try to bind to a reserved port
+ for resvport = 600, 1024, 1 do
+ status, err = socket:bind(nil, 1000)
+ if status then break end
+ end
+ end
end
if (not(status)) then
return status, string.format("%s connect error: %s",
@@ -273,14 +289,30 @@ Comm = {
end
if not auth then
return false, "Comm.CreateHeader: No authentication specified"
- elseif auth.type ~= Portmap.AuthType.NULL then
- return false, "Comm.CreateHeader: invalid authentication type specified"
end
packet = bin.pack( ">IIIIII", xid, Portmap.MessageType.CALL, RPC_VERSION,
self.program_id, self.version, procedure )
if auth.type == Portmap.AuthType.NULL then
packet = packet .. bin.pack( "IIII", 0, 0, 0, 0 )
+ elseif auth.type == Portmap.AuthType.UNIX then
+ packet = packet .. Util.marshall_int32(auth.type)
+ local blob = Util.marshall_int32(nmap.clock()) --time
+ blob = blob .. Util.marshall_vopaque(auth.hostname or 'localhost')
+ blob = blob .. Util.marshall_int32(auth.uid or 0)
+ blob = blob .. Util.marshall_int32(auth.gid or 0)
+ if auth.gids then --len prefix gid list
+ blob = blob .. Util.marshall_int32(#auth.gids)
+ for _,gid in ipairs(auth.gids) do
+ blob = blob .. Util.marshall_int32(gid)
+ end
+ else
+ blob = blob .. Util.marshall_int32(0)
+ end
+ packet = packet .. Util.marshall_vopaque(blob)
+ packet = packet .. bin.pack( "II", 0, 0 ) --AUTH_NULL verf
+ else
+ return false, "Comm.CreateHeader: invalid authentication type specified"
end
return true, packet
end,
@@ -450,7 +482,8 @@ Portmap =
-- TODO: add more Authentication Protocols
AuthType =
{
- NULL = 0
+ NULL = 0,
+ UNIX = 1,
},
-- TODO: complete Authentication stats and error messages
@@ -828,7 +861,7 @@ Mount = {
end
packet = comm:EncodePacket(nil, Mount.Procedure.EXPORT,
- { type=Portmap.AuthType.NULL }, nil )
+ { type=Portmap.AuthType.UNIX }, nil )
if (not(comm:SendPacket( packet ))) then
return false, "Mount.Export: Failed to send data"
end
@@ -965,7 +998,7 @@ Mount = {
data = Util.marshall_vopaque(path)
- packet = comm:EncodePacket( nil, Mount.Procedure.MOUNT, { type=Portmap.AuthType.NULL }, data )
+ packet = comm:EncodePacket( nil, Mount.Procedure.MOUNT, { type=Portmap.AuthType.UNIX }, data )
if (not(comm:SendPacket(packet))) then
return false, "Mount: Failed to send data"
end
@@ -1055,7 +1088,7 @@ Mount = {
data = Util.marshall_vopaque(path)
- packet = comm:EncodePacket( nil, Mount.Procedure.UMNT, { type=Portmap.AuthType.NULL }, data )
+ packet = comm:EncodePacket( nil, Mount.Procedure.UMNT, { type=Portmap.AuthType.UNIX }, data )
if (not(comm:SendPacket(packet))) then
return false, "Unmount: Failed to send data"
end
@@ -1493,7 +1526,7 @@ NFS = {
data = bin.pack("A>I>I", file_handle, cookie, count)
end
packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].READDIR,
- { type=Portmap.AuthType.NULL }, data )
+ { type=Portmap.AuthType.UNIX }, data )
if(not(comm:SendPacket( packet ))) then
return false, "ReadDir: Failed to send data"
end
@@ -1613,7 +1646,7 @@ NFS = {
data = Util.marshall_opaque(dir_handle) .. Util.marshall_vopaque(file)
packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].LOOKUP,
- {type=Portmap.AuthType.NULL}, data)
+ {type=Portmap.AuthType.UNIX}, data)
if(not(comm:SendPacket(packet))) then
return false, "LookUp: Failed to send data"
end
@@ -1790,7 +1823,7 @@ NFS = {
opaque_data, dircount, maxcount)
packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].READDIRPLUS,
- {type = Portmap.AuthType.NULL }, data)
+ {type = Portmap.AuthType.UNIX }, data)
if (not(comm:SendPacket(packet))) then
return false, "ReadDirPlus: Failed to send data"
@@ -1869,7 +1902,7 @@ NFS = {
data = bin.pack("A", file_handle)
packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].FSSTAT,
- {type = Portmap.AuthType.NULL}, data)
+ {type = Portmap.AuthType.UNIX}, data)
if (not(comm:SendPacket(packet))) then
return false, "FsStat: Failed to send data"
@@ -1951,7 +1984,7 @@ NFS = {
data = Util.marshall_opaque(file_handle)
packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].FSINFO,
- {type = Portmap.AuthType.NULL}, data)
+ {type = Portmap.AuthType.UNIX}, data)
if (not(comm:SendPacket(packet))) then
return false, "FsInfo: Failed to send data"
@@ -2030,7 +2063,7 @@ NFS = {
data = Util.marshall_opaque(file_handle)
packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].PATHCONF,
- {type = Portmap.AuthType.NULL}, data)
+ {type = Portmap.AuthType.UNIX}, data)
if (not(comm:SendPacket(packet))) then
return false, "PathConf: Failed to send data"
@@ -2107,7 +2140,7 @@ NFS = {
data = Util.marshall_opaque(file_handle) .. Util.marshall_uint32(access)
packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].ACCESS,
- {type = Portmap.AuthType.NULL}, data)
+ {type = Portmap.AuthType.UNIX}, data)
if (not(comm:SendPacket(packet))) then
return false, "Access: Failed to send data"
@@ -2155,7 +2188,7 @@ NFS = {
end
data = Util.marshall_opaque(file_handle)
- packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].STATFS, { type=Portmap.AuthType.NULL }, data )
+ packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].STATFS, { type=Portmap.AuthType.UNIX }, data )
if (not(comm:SendPacket( packet ))) then
return false, "StatFS: Failed to send data"
end
@@ -2235,7 +2268,7 @@ NFS = {
local data, packet, status, attribs, pos, header
data = Util.marshall_opaque(file_handle)
- packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].GETATTR, { type=Portmap.AuthType.NULL }, data )
+ packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].GETATTR, { type=Portmap.AuthType.UNIX }, data )
if(not(comm:SendPacket(packet))) then
return false, "GetAttr: Failed to send data"
end
diff --git a/scripts/nfs-ls.nse b/scripts/nfs-ls.nse
index 402dfd243..5833b9101 100644
--- a/scripts/nfs-ls.nse
+++ b/scripts/nfs-ls.nse
@@ -4,6 +4,7 @@ local stdnse = require "stdnse"
local string = require "string"
local tab = require "tab"
local table = require "table"
+local nmap = require "nmap"
description = [[
Attempts to get useful information about files from NFS exports.
@@ -55,8 +56,7 @@ These access permissions are shown only with NFSv3:
-- |_ lrwxrwxrwx 1000 1002 8 2010-06-10 08:34 symlink
--
-- @args nfs-ls.maxfiles If set, limits the amount of files returned by
--- the script when using the nfs-ls.dirlist argument.
--- If set to 0
+-- the script. If set to 0
-- or less, all files are shown. The default value is 10.
-- @args nfs-ls.human If set to 1 or true,
-- shows file sizes in a human readable format with suffixes like
@@ -88,19 +88,57 @@ categories = {"discovery", "safe"}
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
+local mountport = nil
+local nfsport = nil
+hostrule = function(host)
+ for _,proto in ipairs({"tcp","udp"}) do
+ local port = nmap.get_ports(host, nil, proto, "open")
+ while port do
+ if port.version then
+ if port.service == "mountd" then
+ mountport = port
+ elseif port.service == "nfs" then
+ nfsport = port
+ end
+ end
+ if mountport and nfsport then break end
+ port = nmap.get_ports(host, port, proto, "open")
+ end
+ if mountport and nfsport then break end
+ end
+ if nfsport == nil then return false end
+ if nfsport.version.rpc_highver == 4 and nfsport.version.rpc_lowver <= 3 then
+ nfsport.version.rpc_goodver = 3
+ else
+ nfsport.version.rpc_goodver = nfsport.version.rpc_highver
+ end
+ return (mountport and nfsport)
+end
+
+local procedures = { }
+
local function table_attributes(nfs, mount, attr)
local file = {}
- file.type = rpc.Util.FtypeToChar(attr.mode)
- file.mode = rpc.Util.FpermToString(attr.mode)
- file.uid = tostring(attr.uid)
- file.gid = tostring(attr.gid)
- if nfs.human then
- file.size = rpc.Util.SizeToHuman(attr.size)
+ if attr.mode then
+ file.type = rpc.Util.FtypeToChar(attr.mode)
+ file.mode = rpc.Util.FpermToString(attr.mode)
+ file.uid = tostring(attr.uid)
+ file.gid = tostring(attr.gid)
+ if nfs.human then
+ file.size = rpc.Util.SizeToHuman(attr.size)
+ else
+ file.size = tostring(attr.size)
+ end
+ file.time = rpc.Util.TimeToString(attr[nfs.time].seconds)
else
- file.size = tostring(attr.size)
+ file.type = '?'
+ file.mode = '?????????'
+ file.uid = '?'
+ file.gid = '?'
+ file.size = '?'
+ file.time = '?'
end
- file.time = rpc.Util.TimeToString(attr[nfs.time].seconds)
file.filename = mount
return file
@@ -145,12 +183,12 @@ local function nfs_ls(nfs, mount, results, access)
local nfsobj = rpc.NFS:new()
local mnt_comm, nfs_comm, fhandle
- mnt_comm, fhandle = rpc.Helper.MountPath(nfs.host, nfs.port, mount)
+ mnt_comm, fhandle = procedures.MountPath(nfs.host, mount)
if mnt_comm == nil then
return false, fhandle
end
- local nfs_comm, status = rpc.Helper.NfsOpen(nfs.host, nfs.port)
+ local nfs_comm, status = procedures.NfsOpen(nfs.host)
if nfs_comm == nil then
rpc.Helper.UnmountPath(mnt_comm, mount)
return false, status
@@ -243,12 +281,11 @@ local function report(nfs, table)
return tab.dump(outtab)
end
-action = function(host, port)
+local mainaction = function(host)
local o, results, mounts, status = {}, {}, {}
local nfs_info =
{
host = host,
- port = port,
--recurs = tonumber(nmap.registry.args['nfs-ls.recurs']) or 1,
}
@@ -275,9 +312,13 @@ action = function(host, port)
table.insert(o, args)
end
- status, mounts = rpc.Helper.ShowMounts(nfs_info.host, nfs_info.port)
+ status, mounts = procedures.ShowMounts(nfs_info.host)
if not status or mounts == nil then
- return stdnse.format_output(false, mounts)
+ if mounts then
+ return stdnse.format_output(false, mounts)
+ else
+ return stdnse.format_output(false, "Mount error")
+ end
end
for _, v in ipairs(mounts) do
@@ -298,3 +339,85 @@ action = function(host, port)
return stdnse.format_output(true, o)
end
+
+hostaction = function(host)
+ procedures = {
+ ShowMounts = function(ahost)
+ local mnt_comm, status, result, mounts
+ local mnt = rpc.Mount:new()
+ mnt_comm = rpc.Comm:new('mountd', mountport.version.rpc_highver)
+ status, result = mnt_comm:Connect(ahost, mountport)
+ if ( not(status) ) then
+ stdnse.print_debug(4, "ShowMounts: %s", result)
+ return false, result
+ end
+ status, mounts = mnt:Export(mnt_comm)
+ mnt_comm:Disconnect()
+ if ( not(status) ) then
+ stdnse.print_debug(4, "ShowMounts: %s", mounts)
+ end
+ return status, mounts
+ end,
+
+ MountPath = function(ahost, path)
+ local fhandle, status, err
+ local mountd, mnt_comm
+ local mnt = rpc.Mount:new()
+
+ mnt_comm = rpc.Comm:new("mountd", mountport.version.rpc_highver)
+
+ status, err = mnt_comm:Connect(host, mountport)
+ if not status then
+ stdnse.print_debug(4, "MountPath: %s", err)
+ return nil, err
+ end
+
+ status, fhandle = mnt:Mount(mnt_comm, path)
+ if not status then
+ mnt_comm:Disconnect()
+ stdnse.print_debug(4, "MountPath: %s", fhandle)
+ return nil, fhandle
+ end
+
+ return mnt_comm, fhandle
+ end,
+
+ NfsOpen = function(ahost)
+ local nfs_comm, status, err
+
+ nfs_comm = rpc.Comm:new('nfs', nfsport.version.rpc_goodver)
+ status, err = nfs_comm:Connect(host, nfsport)
+ if not status then
+ stdnse.print_debug(4, "NfsOpen: %s", err)
+ return nil, err
+ end
+
+ return nfs_comm, nil
+ end,
+ }
+ return mainaction(host)
+end
+
+portaction = function(host, port)
+ procedures = {
+ ShowMounts = function(ahost)
+ return rpc.Helper.ShowMounts(ahost, port)
+ end,
+ MountPath = function(ahost, path)
+ return rpc.Helper.MountPath(ahost, port, path)
+ end,
+ NfsOpen = function(ahost)
+ return rpc.Helper.NfsOpen(ahost, port)
+ end,
+ }
+ return mainaction(host)
+end
+
+local ActionsTable = {
+ -- portrule: use rpcbind service
+ portrule = portaction,
+ -- hostrule: Talk to services directly
+ hostrule = hostaction
+}
+
+action = function(...) return ActionsTable[SCRIPT_TYPE](...) end
diff --git a/scripts/nfs-showmount.nse b/scripts/nfs-showmount.nse
index 8f6ae8042..182eb53bf 100644
--- a/scripts/nfs-showmount.nse
+++ b/scripts/nfs-showmount.nse
@@ -32,14 +32,34 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}
-portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
+portrule = shortport.port_or_service(111, {"rpcbind", "mountd"}, {"tcp", "udp"} )
+
+local function get_exports(host, port)
+ local mnt = rpc.Mount:new()
+ mnt_comm = rpc.Comm:new('mountd', port.version.rpc_highver)
+ status, result = mnt_comm:Connect(host, port)
+ if ( not(status) ) then
+ stdnse.print_debug(4, "get_exports: %s", result)
+ return false, result
+ end
+ status, mounts = mnt:Export(mnt_comm)
+ mnt_comm:Disconnect()
+ if ( not(status) ) then
+ stdnse.print_debug(4, "get_exports: %s", mounts)
+ end
+ return status, mounts
+end
action = function(host, port)
local status, mounts, proto
local result = {}
- status, mounts = rpc.Helper.ShowMounts( host, port )
+ if port.service == "mountd" then
+ status, mounts = get_exports( host, port )
+ else
+ status, mounts = rpc.Helper.ShowMounts( host, port )
+ end
if not status or mounts == nil then
return stdnse.format_output(false, mounts)
diff --git a/scripts/nfs-statfs.nse b/scripts/nfs-statfs.nse
index 844db48f7..386e83881 100644
--- a/scripts/nfs-statfs.nse
+++ b/scripts/nfs-statfs.nse
@@ -4,6 +4,7 @@ local stdnse = require "stdnse"
local string = require "string"
local tab = require "tab"
local table = require "table"
+local nmap = require "nmap"
description = [[
Retrieves disk space statistics and information from a remote NFS share.
@@ -40,6 +41,35 @@ categories = {"discovery", "safe"}
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
+local mountport = nil
+local nfsport = nil
+hostrule = function(host)
+ for _,proto in ipairs({"tcp","udp"}) do
+ local port = nmap.get_ports(host, nil, proto, "open")
+ while port do
+ if port.version then
+ if port.service == "mountd" then
+ mountport = port
+ elseif port.service == "nfs" then
+ nfsport = port
+ end
+ end
+ if mountport and nfsport then break end
+ port = nmap.get_ports(host, port, proto, "open")
+ end
+ if mountport and nfsport then break end
+ end
+ if nfsport == nil then return false end
+ if nfsport.version.rpc_highver == 4 and nfsport.version.rpc_lowver <= 3 then
+ nfsport.version.rpc_goodver = 3
+ else
+ nfsport.version.rpc_goodver = nfsport.version.rpc_highver
+ end
+ return (mountport and nfsport)
+end
+
+local procedures = { }
+
local function table_fsstat(nfs, mount, stats)
local fs, err = rpc.Util.calc_fsstat_table(stats, nfs.version, nfs.human)
if fs == nil then
@@ -111,12 +141,12 @@ local function nfs_filesystem_info(nfs, mount, filesystem)
local nfsobj = rpc.NFS:new()
local mnt_comm, nfs_comm, fhandle
- mnt_comm, fhandle = rpc.Helper.MountPath(nfs.host, nfs.port, mount)
+ mnt_comm, fhandle = procedures.MountPath(nfs.host, mount)
if mnt_comm == nil then
return false, fhandle
end
- local nfs_comm, status = rpc.Helper.NfsOpen(nfs.host, nfs.port)
+ local nfs_comm, status = procedures.NfsOpen(nfs.host)
if nfs_comm == nil then
rpc.Helper.UnmountPath(mnt_comm, mount)
return false, status
@@ -124,8 +154,8 @@ local function nfs_filesystem_info(nfs, mount, filesystem)
nfs.version = nfs_comm.version
- -- use simple check since NFSv1 is not used anymore.
- if (mnt_comm.version ~= nfs_comm.version) then
+ -- use simple check since NFSv1 is not used anymore, and NFSv4 not supported
+ if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
rpc.Helper.UnmountPath(mnt_comm, mount)
return false, string.format("versions mismatch, nfs v%d - mount v%d",
nfs_comm.version, mnt_comm.version)
@@ -179,16 +209,15 @@ local function nfs_filesystem_info(nfs, mount, filesystem)
return true, nil
end
-action = function(host, port)
+mainaction = function(host)
local fs_info, mounts, status = {}, {}, {}
local nfs_info =
{
host = host,
- port = port,
}
nfs_info.human = stdnse.get_script_args('nfs-statfs.human')
- status, mounts = rpc.Helper.ShowMounts( host, port )
+ status, mounts = procedures.ShowMounts( host )
if (not(status)) then
return stdnse.format_output(false, mounts)
end
@@ -204,3 +233,85 @@ action = function(host, port)
return stdnse.format_output(true, report(nfs_info, fs_info))
end
+
+hostaction = function(host)
+ procedures = {
+ ShowMounts = function(ahost)
+ local mnt_comm, status, result, mounts
+ local mnt = rpc.Mount:new()
+ mnt_comm = rpc.Comm:new('mountd', mountport.version.rpc_highver)
+ status, result = mnt_comm:Connect(ahost, mountport)
+ if ( not(status) ) then
+ stdnse.print_debug(4, "ShowMounts: %s", result)
+ return false, result
+ end
+ status, mounts = mnt:Export(mnt_comm)
+ mnt_comm:Disconnect()
+ if ( not(status) ) then
+ stdnse.print_debug(4, "ShowMounts: %s", mounts)
+ end
+ return status, mounts
+ end,
+
+ MountPath = function(ahost, path)
+ local fhandle, status, err
+ local mountd, mnt_comm
+ local mnt = rpc.Mount:new()
+
+ mnt_comm = rpc.Comm:new("mountd", mountport.version.rpc_highver)
+
+ status, err = mnt_comm:Connect(host, mountport)
+ if not status then
+ stdnse.print_debug(4, "MountPath: %s", err)
+ return nil, err
+ end
+
+ status, fhandle = mnt:Mount(mnt_comm, path)
+ if not status then
+ mnt_comm:Disconnect()
+ stdnse.print_debug(4, "MountPath: %s", fhandle)
+ return nil, fhandle
+ end
+
+ return mnt_comm, fhandle
+ end,
+
+ NfsOpen = function(ahost)
+ local nfs_comm, status, err
+
+ nfs_comm = rpc.Comm:new('nfs', nfsport.version.rpc_goodver)
+ status, err = nfs_comm:Connect(host, nfsport)
+ if not status then
+ stdnse.print_debug(4, "NfsOpen: %s", err)
+ return nil, err
+ end
+
+ return nfs_comm, nil
+ end,
+ }
+ return mainaction(host)
+end
+
+portaction = function(host, port)
+ procedures = {
+ ShowMounts = function(ahost)
+ return rpc.Helper.ShowMounts(ahost, port)
+ end,
+ MountPath = function(ahost, path)
+ return rpc.Helper.MountPath(ahost, port, path)
+ end,
+ NfsOpen = function(ahost)
+ return rpc.Helper.NfsOpen(ahost, port)
+ end,
+ }
+ return mainaction(host)
+end
+
+local ActionsTable = {
+ -- portrule: use rpcbind service
+ portrule = portaction,
+ -- hostrule: Talk to services directly
+ hostrule = hostaction
+}
+
+action = function(...) return ActionsTable[SCRIPT_TYPE](...) end