local bit = require 'bit' local smb = require 'smb' local string = require 'string' local stdnse = require 'stdnse' local ls = require 'ls' local openssl= stdnse.silent_require 'openssl' description = [[ Attempts to retrieve useful information about files shared on SMB volumes. The output is intended to resemble the output of the UNIX ls command. ]] --- -- @usage -- nmap -p 445 --script smb-ls --script-args 'share=c$,path=\temp' -- nmap -p 445 --script smb-enum-shares,smb-ls -- -- @args smb-ls.share (or smb-ls.shares) the share (or a colon-separated list -- of shares) to connect to (default: use shares found by smb-enum-shares) -- @args smb-ls.path the path, relative to the share to list the contents from -- (default: root of the share) -- @args smb-ls.pattern the search pattern to execute (default: *) -- @args smb-ls.checksum download each file and calculate a checksum -- (default: false) -- -- @output -- Host script results: -- | smb-ls: -- | Volume \\192.168.56.101\c$\ -- | SIZE TIME FILENAME -- | 0 2007-12-02 00:20:09 AUTOEXEC.BAT -- | 0 2007-12-02 00:20:09 CONFIG.SYS -- | 2007-12-02 00:53:39 Documents and Settings -- | 2009-09-08 13:26:10 e5a6b742d36facb19c5192852c43 -- | 2008-12-01 02:06:29 Inetpub -- | 94720 2007-02-18 00:31:38 msizap.exe -- | 2007-12-02 00:55:01 Program Files -- | 2008-12-01 02:05:52 temp -- | 2011-12-16 14:40:18 usr -- | 2007-12-02 00:42:40 WINDOWS -- | 2007-12-02 00:22:38 wmpub -- |_ -- -- @xmloutput -- --
--
--
-- 0 -- 2007-12-02 00:20:09 -- AUTOEXEC.BAT --
-- -- 0 -- 2007-12-02 00:20:09 -- CONFIG.SYS --
-- -- <DIR> -- 2007-12-02 00:53:39 -- Documents and Settings --
-- -- <DIR> -- 2009-09-08 13:26:10 -- e5a6b742d36facb19c5192852c43 --
-- -- <DIR> -- 2008-12-01 02:06:29 -- Inetpub --
-- -- 94720 -- 2007-02-18 00:31:38 -- msizap.exe --
-- -- <DIR> -- 2007-12-02 00:55:01 -- Program Files --
-- -- <DIR> -- 2008-12-01 02:05:52 -- temp --
-- -- <DIR> -- 2011-12-16 14:40:18 -- usr --
-- -- <DIR> -- 2007-12-02 00:42:40 -- WINDOWS --
-- -- <DIR> -- 2007-12-02 00:22:38 -- wmpub --
-- -- \\192.168.1.2\Downloads -- -- author = "Patrik Karlsson" license = "Same as Nmap--See https://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') or '\\' local arg_pattern = stdnse.get_script_args(SCRIPT_NAME .. '.pattern') or '*' hostrule = function(host) 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 local function is_dir(fe) return ( bit.band(fe.attrs, 16) == 16 ) end local function list_files(host, share, smbstate, path, options, output, maxdepth, basedir) basedir = basedir or "" local continue for fe in smb.find_files(smbstate, path .. '\\' .. arg_pattern, options) do if basedir == "" or (fe.fname ~= "." and fe.fname ~= "..") then if ls.config('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 "" continue = ls.add_file(output, {is_dir(fe) and '' or fe.eof, fe.created, basedir .. fe.fname, sha1}) else continue = ls.add_file(output, {is_dir(fe) and '' or fe.eof, fe.created, basedir .. fe.fname}) end if not continue then return false end if is_dir(fe) then continue = true if maxdepth > 0 then continue = list_files(host, share, smbstate, path .. '\\' .. fe.fname, options, output, maxdepth - 1, basedir .. fe.fname .. '\\') elseif maxdepth < 0 then continue = list_files(host, share, smbstate, path .. '\\' .. fe.fname, options, output, -1, basedir .. fe.fname .. '\\') end if not continue then return false end end end end return true end action = function(host) -- 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 local output = ls.new_listing() for _, share in ipairs(arg_shares) do local status, smbstate = smb.start_ex(host, true, true, share, nil, nil, nil) if ( not(status) ) then ls.report_error( output, ("Failed to authenticate to server (%s) for directory of \\\\%s\\%s%s"):format(smbstate, stdnse.get_hostname(host), share, arg_path)) else -- remove leading slash arg_path = ( arg_path:sub(1,2) == '\\' and arg_path:sub(2) or arg_path ) local options = {} local depth, path, dirs = 0, arg_path, {} local file_count, dir_count, total_bytes = 0, 0, 0 local continue = true ls.new_vol( output, '\\\\' .. stdnse.get_hostname(host) .. '\\' .. share .. path, false) continue = list_files(host, share, smbstate, path, options, output, ls.config('maxdepth')) if not continue then ls.report_info( output, string.format("maxfiles limit reached (%d)", ls.config('maxfiles'))) end ls.end_vol(output) smb.stop(smbstate) end end return ls.end_listing(output) end