Black Hat Python TCP Proxy Rewrite

Filed under python on October 24, 2019

This next section was pretty easy to port. My implementation isn’t as clean as it could be, but for an implementation that took me an hour or so to get working then another hour or so to clean up a little. The repo is here

The Main Problem

Exactly the same as the netcat script. The socket methods don’t return strings any more, so let’s see what we can do about that

The server setup

    def run(self):
        self.__server_socket.bind((self.__server_addr, self.__server_port))
        self.__server_socket.listen(5)
        self.__reader_thread = threading.Thread(target=self.__reader_loop)
        self.__reader_thread.start()
        try:
            while True:
                client, addr = self.__server_socket.accept()
                print("[*] Accepted connection from {}".format(addr))
                target_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    target_sock.connect((self.__target_host, self.__target_port))
                    self.__socket_pairs.append((client, target_sock))
                except Exception as e:
                    print("[!!] Could not connect to target machine")
                    print(e)
                    client.shutdown(socket.SHUT_RDWR)
                    client.close()
        except KeyboardInterrupt:
            print("[!!] Caught keybboard interrupt. Closing down")
        except Exception as e:
            print("[!!] Caught exception")
            print(e)
        finally:
            self.__close = True
            self.__reader_thread.join()
            for (x,y) in self.__socket_pairs:
                x.shutdown(socket.SHUT_RDWR)
                x.close()
                y.shutdown(socket.SHUT_RDWR)
                y.close()
            self.__server_socket.shutdown(socket.SHUT_RDWR)
            self.__server_socket.close()

This is a pretty bog standard server set up. The main difference is that we set up connection pairs for the client and server.

The reader loop

    def __reader_loop(self):
        while not self.__close:
            flatlist = [item for tempList in self.__socket_pairs for item in tempList]
            (rs, _, _) = select.select(flatlist, [], [], 0.1)
            for s in rs:
                matching_sock = None
                pair = None
                for (x, y) in self.__socket_pairs:
                    if x == s:
                        matching_sock = y
                        pair = (x,y)
                        print("[<==] Received message")
                        break
                    if y == s:
                        matching_sock = x
                        pair = (x,y)
                        print("[==>] Received message")
                        break
                raw_buffer = bytearray()
                while True:
                    (rss,_,_) = select.select([s], [], [], 0.001)
                    if len(rss) == 0:
                        break
                    raw_buffer.extend(s.recv(4096))
                if len(raw_buffer) == 0 or raw_buffer == b'\xff\xf4\xff\xfd\x06':
                    self.__socket_pairs.remove(pair)
                    s.shutdown(socket.SHUT_RDWR)
                    s.close()
                    matching_sock.shutdown(socket.SHUT_RDWR)
                    matching_sock.close()
                    continue
                for h in self.__handlers:
                    h.handle_message(raw_buffer)
                matching_sock.send(raw_buffer)

I’m a little worried about this implementation, mainly the buffer part there where we build the whole message before sending it the message handler. Want to rethink that part a little so it scales a bit better and doesn’t blow out its memory on larger messages. May come back to it.

Other than that, all we’re really doing is reading a message off one socket and slapping it on the other. Nothing too fancy.

The other difference from my last script is that I only have two threads running to cut down on the overhead a little. Working with the socket pairs we’re creating, we just iterate over them until we find the right one. I think I might need to refactor this to use a lookup instead to stop all the looping, but no big deal for now while we stuff around with it.

Other stuff

Arg parsing is much the same as the last one, as is the message handler interface. I’ve attached a hex dump to it, but nothing too intense.

class MessageProcessor:
    def handle_message(self, msg):
        pass


class HexDump(MessageProcessor):
    def handle_message(self, msg):
        print("[*] Message Hex:")
        print(binascii.b2a_hex(msg))

We attach these to the server object when we create it

server = ProxyServer(args.target, args.targetport, args.client, args.clientport, [HexDump()])

Closing Thoughts

I don’t think there was much difference between this one and the last one really. Hopefully with the SSH script in the next section we’ll see something new