@@ -33,6 +33,7 @@ def initialize(info={})
33
33
'hdm' ,
34
34
'nebulus' ,
35
35
'sinn3r' ,
36
+ 'altonjx' ,
36
37
'r3dy'
37
38
] ,
38
39
'License' => MSF_LICENSE ,
@@ -44,7 +45,11 @@ def initialize(info={})
44
45
45
46
register_options (
46
47
[
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 ] ) ,
48
53
OptBool . new ( 'USE_SRVSVC_ONLY' , [ true , 'List shares only with SRVSVC' , false ] )
49
54
] , self . class )
50
55
@@ -75,7 +80,7 @@ def to_unix_time(thi, tlo)
75
80
t . strftime ( "%m-%d-%Y %H:%M:%S" )
76
81
end
77
82
78
- def eval_host ( ip , share )
83
+ def eval_host ( ip , share , subdir = "" )
79
84
read = write = false
80
85
81
86
# srvsvc adds a null byte that needs to be removed
@@ -132,7 +137,7 @@ def eval_host(ip, share)
132
137
133
138
return read , write , msg , nil if skip
134
139
135
- rfd = self . simple . client . find_first ( "\\ " )
140
+ rfd = self . simple . client . find_first ( "#{ subdir } \\ * " )
136
141
read = true if rfd != nil
137
142
138
143
# Test writable
@@ -276,57 +281,148 @@ def srvsvc_netshareenum(ip)
276
281
shares
277
282
end
278
283
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
+
279
321
def get_files_info ( ip , rport , shares , info )
322
+
280
323
read = false
281
324
write = false
282
325
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
+
283
335
list = shares . collect { |e | e [ 0 ] }
284
336
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
306
347
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
322
359
end
323
360
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
+ }
324
384
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 )
329
411
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 } " )
330
426
end
331
427
end
332
428
end
@@ -363,7 +459,7 @@ def run_host(ip)
363
459
if shares . empty?
364
460
print_status ( "#{ ip } :#{ rport } - No shares collected" )
365
461
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 ( ", " )
367
463
shares_info . split ( ", " ) . each { |share |
368
464
print_good share
369
465
}
@@ -376,7 +472,7 @@ def run_host(ip)
376
472
:update => :unique_data
377
473
)
378
474
379
- if datastore [ 'DIR_SHARE ' ]
475
+ if datastore [ 'SpiderShares ' ]
380
476
get_files_info ( ip , rport , shares , info )
381
477
end
382
478
0 commit comments