mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Script smb-ls can now use results from smb-enum-shares
- smb-ls parameter `path` is now optional (defaults to '\').
- smb-ls parameter `maxdepth` now defaults to 1 (no recursion)
instead of 0 (infinite recursion).
- smb-ls has a new `shares` parameter to specify a comma-separated
list of shares to browse.
- smb-enum-shares adds found shares to an array in the host
registry, and smb-ls uses this array when no `share` or `shares`
parameter have been specified.
Patch by Pierre LALET <pierre.lalet@cea.fr>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] Make smb-ls able to leverage results from smb-enum-shares or list of
|
||||
shares specified on command line. [Pierre Lalet]
|
||||
|
||||
o [NSE] Fix X509 cert date parsing for dates after 2049. Reported by Teppo
|
||||
Turtiainen. [Daniel Miller]
|
||||
|
||||
|
||||
@@ -126,6 +126,10 @@ action = function(host)
|
||||
end
|
||||
response.account_used = string.format("%s%s", domain, stdnse.string_or_blank(username, '<blank>'))
|
||||
|
||||
if host.registry['smb_shares'] == nil then
|
||||
host.registry['smb_shares'] = {}
|
||||
end
|
||||
|
||||
for i = 1, #shares, 1 do
|
||||
local share = shares[i]
|
||||
local share_output = stdnse.output_table()
|
||||
@@ -135,6 +139,8 @@ action = function(host)
|
||||
-- A share of 'NT_STATUS_OBJECT_NAME_NOT_FOUND' indicates this isn't a fileshare
|
||||
if(share['user_can_write'] == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
|
||||
share_output["Type"] = "Not a file share"
|
||||
else
|
||||
table.insert(host.registry['smb_shares'], share.name)
|
||||
end
|
||||
else
|
||||
local details = share['details']
|
||||
@@ -144,6 +150,12 @@ action = function(host)
|
||||
share_output["Users"] = details.current_users
|
||||
share_output["Max Users"] = details.max_users
|
||||
share_output["Path"] = details.path
|
||||
|
||||
if (share_output["Type"] == "STYPE_DISKTREE" or
|
||||
share_output["Type"] == "STYPE_DISKTREE_TEMPORARY" or
|
||||
share_output["Type"] == "STYPE_DISKTREE_HIDDEN") then
|
||||
table.insert(host.registry['smb_shares'], share.name)
|
||||
end
|
||||
end
|
||||
-- Print details for a file share
|
||||
if(share['anonymous_can_read'] and share['anonymous_can_write']) then
|
||||
@@ -172,6 +184,10 @@ action = function(host)
|
||||
response[share.name] = share_output
|
||||
end
|
||||
|
||||
if next(host.registry['smb_shares']) == nil then
|
||||
host.registry['smb_shares'] = nil
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
@@ -30,10 +30,11 @@ The output is intended to resemble the output of the UNIX <code>ls</code> comman
|
||||
-- | 2007-12-02 00:42:40 <DIR> WINDOWS
|
||||
-- |_ 2007-12-02 00:22:38 <DIR> wmpub
|
||||
--
|
||||
-- @args smb-ls.share the share to connect to
|
||||
-- @args smb-ls.path the path, relative to the share to list the contents from
|
||||
-- @args smb-ls.share [optional] the share to connect to
|
||||
-- @args smb-ls.shares [optional] a colon-separated list of shares to connect to
|
||||
-- @args smb-ls.path [optional] the path, relative to the share to list the contents from
|
||||
-- @args smb-ls.pattern [optional] the search pattern to execute (default: *)
|
||||
-- @args smb-ls.maxdepth [optional] the maximum depth to recurse into a directory
|
||||
-- @args smb-ls.maxdepth [optional] the maximum depth to recurse into a directory (default: no recursion)
|
||||
-- @args smb-ls.maxfiles [optional] return only a certain amount of files
|
||||
-- @args smb-ls.checksum [optional] download each file and calculate a SHA1 checksum
|
||||
--
|
||||
@@ -41,16 +42,20 @@ The output is intended to resemble the output of the UNIX <code>ls</code> comman
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
dependencies = {"smb-enum-shares"}
|
||||
|
||||
local arg_shares = stdnse.get_script_args(SCRIPT_NAME .. '.shares')
|
||||
local arg_share = stdnse.get_script_args(SCRIPT_NAME .. '.share')
|
||||
local arg_path = stdnse.get_script_args(SCRIPT_NAME .. '.path')
|
||||
local arg_path = stdnse.get_script_args(SCRIPT_NAME .. '.path') or '\\'
|
||||
local arg_pattern = stdnse.get_script_args(SCRIPT_NAME .. '.pattern') or '*'
|
||||
local arg_maxfiles = tonumber(stdnse.get_script_args(SCRIPT_NAME .. '.maxfiles'))
|
||||
local arg_maxdepth = tonumber(stdnse.get_script_args(SCRIPT_NAME .. '.maxdepth'))
|
||||
local arg_maxdepth = stdnse.get_script_args(SCRIPT_NAME .. '.maxdepth')
|
||||
local arg_checksum = stdnse.get_script_args(SCRIPT_NAME .. '.checksum')
|
||||
|
||||
hostrule = function(host)
|
||||
return ( smb.get_port(host) ~= nil and arg_share and arg_path )
|
||||
return ( smb.get_port(host) ~= nil and
|
||||
(arg_shares or arg_share
|
||||
or host.registry['smb_shares'] ~= nil) )
|
||||
end
|
||||
|
||||
-- checks whether the file entry is a directory
|
||||
@@ -61,68 +66,99 @@ end
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
|
||||
action = function(host)
|
||||
local status, smbstate = smb.start_ex(host, true, true, arg_share, nil, nil, nil)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to authenticate to server (" .. smbstate .. ")")
|
||||
|
||||
-- give priority to specified shares if specified
|
||||
if arg_shares ~= nil then
|
||||
arg_shares = stdnse.strsplit(":", arg_shares)
|
||||
elseif arg_share ~= nil then
|
||||
arg_shares = {arg_share}
|
||||
else
|
||||
arg_shares = host.registry['smb_shares']
|
||||
end
|
||||
|
||||
-- remove leading slash
|
||||
arg_path = ( arg_path:sub(1,2) == '\\' and arg_path:sub(2) or arg_path )
|
||||
-- arg_maxdepth defaults to 1 (no recursion)
|
||||
if arg_maxdepth == nil then
|
||||
arg_maxdepth = 1
|
||||
else
|
||||
arg_maxdepth = tonumber(arg_maxdepth)
|
||||
end
|
||||
|
||||
-- fixup checksum argument
|
||||
arg_checksum = ( arg_checksum == 'true' or arg_checksum == '1' ) and true or false
|
||||
local output = {}
|
||||
|
||||
local options = { max_depth = arg_maxdepth, max_files = arg_maxfiles }
|
||||
local depth, path, output, dirs = 0, arg_path, {}, {}
|
||||
local file_count, dir_count, total_bytes = 0, 0, 0
|
||||
for _, share in ipairs(arg_shares) do
|
||||
local status, smbstate = smb.start_ex(host, true, true, share,
|
||||
nil, nil, nil)
|
||||
if ( not(status) ) then
|
||||
table.insert(
|
||||
output,
|
||||
("Failed to authenticate to server (%s) for directory of \\\\%s\\%s%s"):format(smbstate, stdnse.get_hostname(host), share, arg_path))
|
||||
table.insert(output, "")
|
||||
else
|
||||
|
||||
repeat
|
||||
local lstab = tab.new((arg_checksum and 4 or 3))
|
||||
table.insert(output, "")
|
||||
|
||||
for fe in smb.find_files(smbstate, path .. '\\' .. arg_pattern, options ) do
|
||||
if ( arg_checksum and not(is_dir(fe)) ) then
|
||||
local status, content = smb.file_read(host, arg_share, path .. '\\' .. fe.fname, nil, {file_create_disposition=1})
|
||||
local sha1 = ( status and stdnse.tohex(openssl.sha1(content)) or "" )
|
||||
tab.addrow(lstab, fe.created, (is_dir(fe) and '<DIR>' or fe.eof), fe.fname, sha1)
|
||||
else
|
||||
tab.addrow(lstab, fe.created, (is_dir(fe) and '<DIR>' or fe.eof), fe.fname)
|
||||
end
|
||||
-- remove leading slash
|
||||
arg_path = ( arg_path:sub(1,2) == '\\' and arg_path:sub(2) or arg_path )
|
||||
|
||||
arg_maxfiles = ( arg_maxfiles and arg_maxfiles - 1 )
|
||||
if ( arg_maxfiles == 0 ) then
|
||||
break
|
||||
end
|
||||
-- fixup checksum argument
|
||||
arg_checksum = ( arg_checksum == 'true' or arg_checksum == '1' ) and true or false
|
||||
|
||||
if ( is_dir(fe) ) then
|
||||
dir_count = dir_count + 1
|
||||
if ( fe.fname ~= '.' and fe.fname ~= '..' ) then
|
||||
table.insert(dirs, { depth = depth + 1, path = path .. '\\' .. fe.fname } )
|
||||
end
|
||||
else
|
||||
total_bytes = total_bytes + fe.eof
|
||||
file_count = file_count + 1
|
||||
end
|
||||
end
|
||||
table.insert(output, { name = ("Directory of %s"):format( '\\\\' .. stdnse.get_hostname(host) .. '\\' .. arg_share .. path), tab.dump(lstab) })
|
||||
local options = { max_depth = arg_maxdepth, max_files = arg_maxfiles }
|
||||
local depth, path, dirs = 0, arg_path, {}
|
||||
local file_count, dir_count, total_bytes = 0, 0, 0
|
||||
|
||||
path = nil
|
||||
if ( #dirs ~= 0 ) then
|
||||
local dir = table.remove(dirs, 1)
|
||||
depth = dir.depth
|
||||
if ( not(arg_maxdepth) or ( dir.depth < arg_maxdepth ) ) then
|
||||
path = dir.path
|
||||
table.insert(output, "")
|
||||
end
|
||||
end
|
||||
until(not(path) or arg_maxfiles == 0)
|
||||
repeat
|
||||
-- we need three columns per row, plus one for checksum if
|
||||
-- requested
|
||||
local lstab = tab.new((arg_checksum and 4 or 3))
|
||||
|
||||
smb.stop(smbstate)
|
||||
for fe in smb.find_files(smbstate, path .. '\\' .. arg_pattern, options ) do
|
||||
if ( arg_checksum and not(is_dir(fe)) ) then
|
||||
local status, content = smb.file_read(host, share, path .. '\\' .. fe.fname, nil, {file_create_disposition=1})
|
||||
local sha1 = ( status and stdnse.tohex(openssl.sha1(content)) or "" )
|
||||
tab.addrow(lstab, fe.created, (is_dir(fe) and '<DIR>' or fe.eof), fe.fname, sha1)
|
||||
else
|
||||
tab.addrow(lstab, fe.created, (is_dir(fe) and '<DIR>' or fe.eof), fe.fname)
|
||||
end
|
||||
|
||||
local summary = { name = "Total Files Listed:",
|
||||
("%8d File(s)\t%d bytes"):format(file_count, total_bytes),
|
||||
("%8d Dir(s)"):format(dir_count) }
|
||||
table.insert(output, "")
|
||||
table.insert(output, summary)
|
||||
arg_maxfiles = ( arg_maxfiles and arg_maxfiles - 1 )
|
||||
if ( arg_maxfiles == 0 ) then
|
||||
break
|
||||
end
|
||||
|
||||
if ( is_dir(fe) ) then
|
||||
dir_count = dir_count + 1
|
||||
if ( fe.fname ~= '.' and fe.fname ~= '..' ) then
|
||||
table.insert(dirs, { depth = depth + 1, path = path .. '\\' .. fe.fname } )
|
||||
end
|
||||
else
|
||||
total_bytes = total_bytes + fe.eof
|
||||
file_count = file_count + 1
|
||||
end
|
||||
end
|
||||
table.insert(output, { name = ("Directory of %s"):format( '\\\\' .. stdnse.get_hostname(host) .. '\\' .. share .. path), tab.dump(lstab) })
|
||||
|
||||
path = nil
|
||||
if ( #dirs ~= 0 ) then
|
||||
local dir = table.remove(dirs, 1)
|
||||
depth = dir.depth
|
||||
if ( not(arg_maxdepth) or ( dir.depth < arg_maxdepth ) ) then
|
||||
path = dir.path
|
||||
table.insert(output, "")
|
||||
end
|
||||
end
|
||||
until(not(path) or arg_maxfiles == 0)
|
||||
|
||||
smb.stop(smbstate)
|
||||
|
||||
local summary = { name = "Total Files Listed:",
|
||||
("%8d File(s)\t%d bytes"):format(file_count, total_bytes),
|
||||
("%8d Dir(s)"):format(dir_count) }
|
||||
table.insert(output, "")
|
||||
table.insert(output, summary)
|
||||
table.insert(output, "")
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user