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