@@ -33,7 +33,8 @@ def initialize(info={})
33
33
'hdm' ,
34
34
'nebulus' ,
35
35
'sinn3r' ,
36
- 'r3dy'
36
+ 'r3dy' ,
37
+ 'altonjx'
37
38
] ,
38
39
'License' => MSF_LICENSE ,
39
40
'DefaultOptions' =>
@@ -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 ( 'ShowFiles' , [ true , 'Show detailed information when spidering' , false ] ) ,
50
+ OptBool . new ( 'SpiderProfiles' , [ false , 'Spider only user profiles when share = C$' , true ] ) ,
51
+ OptEnum . new ( 'LogSpider' , [ false , '0 = disabled, 1 = CSV, 2 = table (txt), 3 = one liner (txt)' , 3 , [ 0 , 1 , 2 , 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
@@ -183,7 +188,7 @@ def lanman_netshareenum(ip, rport, info)
183
188
rescue ::Rex ::Proto ::SMB ::Exceptions ::ErrorCode => e
184
189
if e . error_code == 0xC00000BB
185
190
vprint_error ( "#{ ip } :#{ rport } - Got 0xC00000BB while enumerating shares, switching to srvsvc..." )
186
- datastore [ 'USE_SRVSVC_ONLY' ] = true # Make sure the module is aware of this state
191
+ @srvsvc = true # Make sure the module is aware of this state
187
192
return srvsvc_netshareenum ( ip )
188
193
end
189
194
end
@@ -276,67 +281,157 @@ def srvsvc_netshareenum(ip)
276
281
shares
277
282
end
278
283
284
+ def get_user_dirs ( ip , share , base , sub_dirs )
285
+ dirs = [ ]
286
+ usernames = [ ]
287
+
288
+ begin
289
+ read , write , type , files = eval_host ( ip , share , base )
290
+ # files or type could return nil due to various conditions
291
+ return dirs if files . nil?
292
+ files . each do |f |
293
+ if f [ 0 ] != "." and f [ 0 ] != ".."
294
+ usernames . push ( f [ 0 ] )
295
+ end
296
+ end
297
+ usernames . each do |username |
298
+ sub_dirs . each do |sub_dir |
299
+ dirs . push ( "#{ base } \\ #{ username } \\ #{ sub_dir } " )
300
+ end
301
+ end
302
+ return dirs
303
+ rescue
304
+ return dirs
305
+ end
306
+ end
307
+
308
+ def profile_options ( ip , share )
309
+ old_dirs = [ 'My Documents' , 'Desktop' ]
310
+ new_dirs = [ 'Desktop' , 'Documents' , 'Downloads' , 'Music' , 'Pictures' , 'Videos' ]
311
+
312
+ dirs = get_user_dirs ( ip , share , "Documents and Settings" , old_dirs )
313
+ if dirs . blank?
314
+ dirs = get_user_dirs ( ip , share , "Users" , new_dirs )
315
+ end
316
+ return dirs
317
+ end
318
+
279
319
def get_files_info ( ip , rport , shares , info )
280
320
read = false
281
321
write = false
282
322
323
+ # Creating a separate file for each IP address's results.
324
+ detailed_tbl = Rex ::Ui ::Text ::Table . new (
325
+ 'Header' => "Spidered results for #{ ip } ." ,
326
+ 'Indent' => 1 ,
327
+ 'Columns' => [ 'IP Address' , 'Type' , 'Share' , 'Path' , 'Name' , 'Created' , 'Accessed' , 'Written' , 'Changed' , 'Size' ]
328
+ )
329
+
330
+ logdata = ""
331
+
283
332
list = shares . collect { |e | e [ 0 ] }
284
333
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
- }
306
-
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 ]
334
+ x = x . strip
335
+ if x == "ADMIN$" or x == "IPC$"
336
+ next
337
+ end
338
+ if not datastore [ 'ShowFiles' ]
339
+ print_status ( "#{ ip } :#{ rport } - Spidering #{ x } ." )
340
+ end
341
+ subdirs = [ "" ]
342
+ if x . strip ( ) == "C$" and datastore [ 'SpiderProfiles' ]
343
+ subdirs = profile_options ( ip , x )
344
+ end
345
+ while subdirs . length > 0
346
+ depth = subdirs [ 0 ] . count ( "\\ " )
347
+ if datastore [ 'SpiderProfiles' ] and x == "C$"
348
+ if depth -2 > datastore [ 'MaxDepth' ]
349
+ subdirs . shift
350
+ next
351
+ end
352
+ else
353
+ if depth > datastore [ 'MaxDepth' ]
354
+ subdirs . shift
355
+ next
322
356
end
323
357
end
358
+ read , write , type , files = eval_host ( ip , x , subdirs [ 0 ] )
359
+ if files and ( read or write )
360
+ if files . length < 3
361
+ subdirs . shift
362
+ next
363
+ end
364
+ header = "#{ ip } :#{ rport } "
365
+ if simple . client . default_domain and simple . client . default_name
366
+ header << " \\ \\ #{ simple . client . default_domain } "
367
+ end
368
+ header << "\\ #{ x . sub ( "C$" , "C$\\ " ) } " if simple . client . default_name
369
+ header << subdirs [ 0 ]
324
370
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 } " )
371
+ pretty_tbl = Rex ::Ui ::Text ::Table . new (
372
+ 'Header' => header ,
373
+ 'Indent' => 1 ,
374
+ 'Columns' => [ 'Type' , 'Name' , 'Created' , 'Accessed' , 'Written' , 'Changed' , 'Size' ]
375
+ )
376
+
377
+ f_types = {
378
+ 1 => 'RO' , 2 => 'HIDDEN' , 4 => 'SYS' , 8 => 'VOL' ,
379
+ 16 => 'DIR' , 32 => 'ARC' , 64 => 'DEV' , 128 => 'FILE'
380
+ }
381
+
382
+ files . each do |file |
383
+ if file [ 0 ] and file [ 0 ] != '.' and file [ 0 ] != '..'
384
+ info = file [ 1 ] [ 'info' ]
385
+ fa = f_types [ file [ 1 ] [ 'attr' ] ] # Item type
386
+ fname = file [ 0 ] # Filename
387
+ tcr = to_unix_time ( info [ 3 ] , info [ 2 ] ) # Created
388
+ tac = to_unix_time ( info [ 5 ] , info [ 4 ] ) # Accessed
389
+ twr = to_unix_time ( info [ 7 ] , info [ 6 ] ) # Written
390
+ tch = to_unix_time ( info [ 9 ] , info [ 8 ] ) # Changed
391
+ sz = info [ 12 ] + info [ 13 ] # Size
392
+
393
+ # Filename is too long for the UI table, cut it.
394
+ fname = "#{ fname [ 0 , 35 ] } ..." if fname . length > 35
395
+
396
+ # Add subdirectories to list to use if SpiderShare is enabled.
397
+ if fa == "DIR" or ( fa == nil and sz == 0 )
398
+ subdirs . push ( subdirs [ 0 ] + "\\ " + fname )
399
+ end
400
+
401
+ pretty_tbl << [ fa || 'Unknown' , fname , tcr , tac , twr , tch , sz ]
402
+ detailed_tbl << [ "#{ ip } " , fa || 'Unknown' , "#{ x } " , subdirs [ 0 ] + "\\ " , fname , tcr , tac , twr , tch , sz ]
403
+ logdata << "#{ ip } \\ #{ x . sub ( "C$" , "C$\\ " ) } #{ subdirs [ 0 ] } \\ #{ fname } \n "
404
+
405
+ end
406
+ end
407
+ print_good ( pretty_tbl . to_s ) if datastore [ 'ShowFiles' ]
329
408
end
409
+ subdirs . shift
410
+ end
411
+ print_status ( "#{ ip } :#{ rport } - Spider #{ x } complete." ) unless datastore [ 'ShowFiles' ] == true
412
+ end
413
+ unless detailed_tbl . rows . empty?
414
+ if datastore [ 'LogSpider' ] == '1'
415
+ p = store_loot ( 'smb.enumshares' , 'text/csv' , ip , detailed_tbl . to_csv )
416
+ print_good ( "#{ ip } - info saved in: #{ p . to_s } " )
417
+ elsif datastore [ 'LogSpider' ] == '2'
418
+ p = store_loot ( 'smb.enumshares' , 'text' , ip , detailed_tbl )
419
+ print_good ( "#{ ip } - info saved in: #{ p . to_s } " )
420
+ elsif datastore [ 'LogSpider' ] == '3'
421
+ p = store_loot ( 'smb.enumshares' , 'text' , ip , logdata )
422
+ print_good ( "#{ ip } - info saved in: #{ p . to_s } " )
330
423
end
331
424
end
332
425
end
333
426
334
- def cleanup
335
- datastore [ 'RPORT' ] = @rport
336
- datastore [ 'SMBDirect' ] = @smb_redirect
337
- datastore [ 'USE_SRVSVC_ONLY' ] = @srvsvc
427
+ def rport
428
+ @rport || datastore [ 'RPORT' ]
338
429
end
339
430
431
+ # Overrides the one in smb.rb
432
+ def smb_direct
433
+ @smb_redirect || datastore [ 'SMBDirect' ]
434
+ end
340
435
341
436
def run_host ( ip )
342
437
@rport = datastore [ 'RPORT' ]
@@ -345,13 +440,13 @@ def run_host(ip)
345
440
shares = [ ]
346
441
347
442
[ [ 139 , false ] , [ 445 , true ] ] . each do |info |
348
- datastore [ 'RPORT' ] = info [ 0 ]
349
- datastore [ 'SMBDirect' ] = info [ 1 ]
443
+ @rport = info [ 0 ]
444
+ @smb_redirect = info [ 1 ]
350
445
351
446
begin
352
447
connect
353
448
smb_login
354
- if datastore [ 'USE_SRVSVC_ONLY' ]
449
+ if @srvsvc
355
450
shares = srvsvc_netshareenum ( ip )
356
451
else
357
452
shares = lanman_netshareenum ( ip , rport , info )
@@ -363,7 +458,7 @@ def run_host(ip)
363
458
if shares . empty?
364
459
print_status ( "#{ ip } :#{ rport } - No shares collected" )
365
460
else
366
- shares_info = shares . map { |x | "#{ ip } : #{ x [ 0 ] } - (#{ x [ 1 ] } ) #{ x [ 2 ] } " } . join ( ", " )
461
+ shares_info = shares . map { |x | "#{ ip } :#{ rport } - #{ x [ 0 ] } - (#{ x [ 1 ] } ) #{ x [ 2 ] } " } . join ( ", " )
367
462
shares_info . split ( ", " ) . each { |share |
368
463
print_good share
369
464
}
@@ -376,7 +471,7 @@ def run_host(ip)
376
471
:update => :unique_data
377
472
)
378
473
379
- if datastore [ 'DIR_SHARE ' ]
474
+ if datastore [ 'SpiderShares ' ]
380
475
get_files_info ( ip , rport , shares , info )
381
476
end
382
477
@@ -414,3 +509,4 @@ def run_host(ip)
414
509
end
415
510
end
416
511
end
512
+
0 commit comments