Skip to content

Commit 51c488a

Browse files
committed
Added smb_enumshares.
1 parent 27ef12b commit 51c488a

File tree

1 file changed

+141
-45
lines changed

1 file changed

+141
-45
lines changed

modules/auxiliary/scanner/smb/smb_enumshares.rb

Lines changed: 141 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def initialize(info={})
3333
'hdm',
3434
'nebulus',
3535
'sinn3r',
36+
'altonjx',
3637
'r3dy'
3738
],
3839
'License' => MSF_LICENSE,
@@ -44,7 +45,11 @@ def initialize(info={})
4445

4546
register_options(
4647
[
47-
OptBool.new('DIR_SHARE', [true, 'Show all the folders and files', false ]),
48+
OptBool.new('SpiderShares', [false, 'Spider shares recursively', false]),
49+
OptBool.new('VERBOSE', [true, 'Show detailed information when spidering', true]),
50+
OptBool.new('SpiderProfiles', [false, 'Spider only user profiles when share = C$', true]),
51+
OptInt.new('LogSpider', [false, '1 = CSV, 2 = table (txt), 3 = one liner (txt)', 3]),
52+
OptInt.new('MaxDepth', [true, 'Max number of subdirectories to spider', 999]),
4853
OptBool.new('USE_SRVSVC_ONLY', [true, 'List shares only with SRVSVC', false ])
4954
], self.class)
5055

@@ -75,7 +80,7 @@ def to_unix_time(thi, tlo)
7580
t.strftime("%m-%d-%Y %H:%M:%S")
7681
end
7782

78-
def eval_host(ip, share)
83+
def eval_host(ip, share, subdir="")
7984
read = write = false
8085

8186
# srvsvc adds a null byte that needs to be removed
@@ -132,7 +137,7 @@ def eval_host(ip, share)
132137

133138
return read,write,msg,nil if skip
134139

135-
rfd = self.simple.client.find_first("\\")
140+
rfd = self.simple.client.find_first("#{subdir}\\*")
136141
read = true if rfd != nil
137142

138143
# Test writable
@@ -276,57 +281,148 @@ def srvsvc_netshareenum(ip)
276281
shares
277282
end
278283

284+
def profile_options(ip, share)
285+
usernames = []
286+
old_dirs = ['My Documents','Desktop']
287+
new_dirs = ['Desktop','Documents','Downloads','Music','Pictures','Videos']
288+
subdirs = []
289+
begin
290+
read,write,type,files = eval_host(ip, share, "Documents and Settings")
291+
files.each do |f|
292+
if f[0] != "." and f[0] != ".."
293+
usernames.push(f[0])
294+
end
295+
end
296+
297+
# Return usernames along with their profile directories.
298+
usernames.each do |username|
299+
old_dirs.each do |dir|
300+
subdirs.push("Documents and Settings\\#{username}\\#{dir}")
301+
end
302+
end
303+
rescue
304+
read,write,type,files = eval_host(ip, share, "Users")
305+
files.each do |f|
306+
if f[0] != "." and f[0] != ".."
307+
usernames.push(f[0])
308+
end
309+
end
310+
311+
# Return usernames along with their profile directories.
312+
usernames.each do |username|
313+
new_dirs.each do |dir|
314+
subdirs.push("Users\\#{username}\\#{dir}")
315+
end
316+
end
317+
end
318+
return subdirs
319+
end
320+
279321
def get_files_info(ip, rport, shares, info)
322+
280323
read = false
281324
write = false
282325

326+
# Creating a separate file for each IP address's results.
327+
detailed_tbl = Rex::Ui::Text::Table.new(
328+
'Header' => "Spidered results for #{ip}.",
329+
'Indent' => 1,
330+
'Columns' => [ 'IP Address', 'Type', 'Share', 'Path', 'Name', 'Created', 'Accessed', 'Written', 'Changed', 'Size' ]
331+
)
332+
333+
logdata = ""
334+
283335
list = shares.collect {|e| e[0]}
284336
list.each do |x|
285-
read,write,type,files = eval_host(ip, x)
286-
if files and (read or write)
287-
header = "#{ip}:#{rport}"
288-
if simple.client.default_domain and simple.client.default_name
289-
header << " \\\\#{simple.client.default_domain}"
290-
end
291-
header << "\\#{simple.client.default_name}\\#{x}" if simple.client.default_name
292-
header << " (#{type})" if type
293-
header << " Readable" if read
294-
header << " Writable" if write
295-
296-
tbl = Rex::Ui::Text::Table.new(
297-
'Header' => header,
298-
'Indent' => 1,
299-
'Columns' => [ 'Type', 'Name', 'Created', 'Accessed', 'Written', 'Changed', 'Size' ]
300-
)
301-
302-
f_types = {
303-
1 => 'RO', 2 => 'HIDDEN', 4 => 'SYS', 8 => 'VOL',
304-
16 => 'DIR', 32 => 'ARC', 64 => 'DEV', 128 => 'FILE'
305-
}
337+
if x.strip == "ADMIN$"
338+
next
339+
end
340+
if not datastore['VERBOSE']
341+
print_status("#{ip}:#{rport} - Spidering #{x}.")
342+
end
343+
subdirs = [""]
344+
if x.strip() == "C$" and datastore['SpiderProfiles']
345+
subdirs = profile_options(ip, x)
346+
end
306347

307-
files.each do |file|
308-
if file[0] and file[0] != '.' and file[0] != '..'
309-
info = file[1]['info']
310-
fa = f_types[file[1]['attr']] # Item type
311-
fname = file[0] # Filename
312-
tcr = to_unix_time(info[3], info[2]) # Created
313-
tac = to_unix_time(info[5], info[4]) # Accessed
314-
twr = to_unix_time(info[7], info[6]) # Written
315-
tch = to_unix_time(info[9], info[8]) # Changed
316-
sz = info[12] + info[13] # Size
317-
318-
# Filename is too long for the UI table, cut it.
319-
fname = "#{fname[0, 35]}..." if fname.length > 35
320-
321-
tbl << [fa || 'Unknown', fname, tcr, tac, twr, tch, sz]
348+
while subdirs.length > 0
349+
depth = subdirs[0].count("\\")
350+
if datastore['SpiderProfiles'] and x == "C$"
351+
if depth-2 > datastore['MaxDepth']
352+
subdirs.shift
353+
next
354+
end
355+
else
356+
if depth > datastore['MaxDepth']
357+
subdirs.shift
358+
next
322359
end
323360
end
361+
read,write,type,files = eval_host(ip, x, subdirs[0])
362+
if files and (read or write)
363+
if files.length < 3
364+
subdirs.shift
365+
next
366+
end
367+
header = "#{ip}:#{rport}"
368+
if simple.client.default_domain and simple.client.default_name
369+
header << " \\\\#{simple.client.default_domain}"
370+
end
371+
header << "\\#{x.sub("C$","C$\\")}" if simple.client.default_name
372+
header << subdirs[0]
373+
374+
pretty_tbl = Rex::Ui::Text::Table.new(
375+
'Header' => header,
376+
'Indent' => 1,
377+
'Columns' => [ 'Type', 'Name', 'Created', 'Accessed', 'Written', 'Changed', 'Size' ]
378+
)
379+
380+
f_types = {
381+
1 => 'RO', 2 => 'HIDDEN', 4 => 'SYS', 8 => 'VOL',
382+
16 => 'DIR', 32 => 'ARC', 64 => 'DEV', 128 => 'FILE'
383+
}
324384

325-
print_good(tbl.to_s)
326-
unless tbl.rows.empty?
327-
p = store_loot('smb.shares', 'text/csv', ip, tbl.to_csv)
328-
print_good("#{x} info saved in: #{p.to_s}")
385+
files.each do |file|
386+
if file[0] and file[0] != '.' and file[0] != '..'
387+
info = file[1]['info']
388+
fa = f_types[file[1]['attr']] # Item type
389+
fname = file[0] # Filename
390+
tcr = to_unix_time(info[3], info[2]) # Created
391+
tac = to_unix_time(info[5], info[4]) # Accessed
392+
twr = to_unix_time(info[7], info[6]) # Written
393+
tch = to_unix_time(info[9], info[8]) # Changed
394+
sz = info[12] + info[13] # Size
395+
396+
# Filename is too long for the UI table, cut it.
397+
fname = "#{fname[0, 35]}..." if fname.length > 35
398+
399+
# Add subdirectories to list to use if SpiderShare is enabled.
400+
if fa == "DIR" or (fa == nil and sz == 0)
401+
subdirs.push(subdirs[0] + "\\" + fname)
402+
end
403+
404+
pretty_tbl << [fa || 'Unknown', fname, tcr, tac, twr, tch, sz]
405+
detailed_tbl << ["#{ip}", fa || 'Unknown', "#{x}", subdirs[0] + "\\", fname, tcr, tac, twr, tch, sz]
406+
logdata << "#{ip}\\#{x.sub("C$","C$\\")}#{subdirs[0]}\\#{fname}\n"
407+
408+
end
409+
end
410+
vprint_good(pretty_tbl.to_s)
329411
end
412+
subdirs.shift
413+
end
414+
print_status("#{ip}:#{rport} - Spider #{x} complete.") unless datastore['VERBOSE'] == true
415+
end
416+
unless detailed_tbl.rows.empty?
417+
if datastore['LogSpider'] == 1
418+
p = store_loot('smb.enumshares', 'text/csv', ip, detailed_tbl.to_csv)
419+
print_good("#{ip} - info saved in: #{p.to_s}")
420+
elsif datastore['LogSpider'] == 2
421+
p = store_loot('smb.enumshares', 'text', ip, detailed_tbl)
422+
print_good("#{ip} - info saved in: #{p.to_s}")
423+
elsif datastore['LogSpider'] == 3
424+
p = store_loot('smb.enumshares', 'text', ip, logdata)
425+
print_good("#{ip} - info saved in: #{p.to_s}")
330426
end
331427
end
332428
end
@@ -363,7 +459,7 @@ def run_host(ip)
363459
if shares.empty?
364460
print_status("#{ip}:#{rport} - No shares collected")
365461
else
366-
shares_info = shares.map{|x| "#{ip}: #{x[0]} - (#{x[1]}) #{x[2]}" }.join(", ")
462+
shares_info = shares.map{|x| "#{ip}:#{rport} - #{x[0]} - (#{x[1]}) #{x[2]}" }.join(", ")
367463
shares_info.split(", ").each { |share|
368464
print_good share
369465
}
@@ -376,7 +472,7 @@ def run_host(ip)
376472
:update => :unique_data
377473
)
378474

379-
if datastore['DIR_SHARE']
475+
if datastore['SpiderShares']
380476
get_files_info(ip, rport, shares, info)
381477
end
382478

0 commit comments

Comments
 (0)