Python Simple Chat through Proxy

03 March 2020

Intro :flashlight:

Simple 1 on 1 chats are too boring since we’ve tried a multiperson chat before, so let’s try a 1:1 chat, but through a proxy, specifically a reverse proxy! This adds an additional layer of anonimity to our servers since we hide our location which adds security!

Concept

What’s a reverse proxy?

Source: linuxbabe

The idea of a reverse proxy is for use with a server, it hides our server’s IP (as long as the proxy isn’t compromised) but still allows clients to connect to our server and have a 2-way data transfer because the proxy itself is also a server, a proxy server.

Demo

Here’s what a normal client-server connection looks like.

The problem with this, is that if a malicious user connects, the attacker will be able to attack our server immediately, so to counter that, here’s what we’ll be making.

As you can see, our client isn’t actually connecting to the real server (on port 1234), but it’s connecting to the proxy server (on port 4321), which is connected to the real server. We can also add additional layers of security on our proxy server such as filters, ip blocking, timeouts, etc. or even more advanced stuff like load balancing.

Note that this is an example scenario, a real scenario would be where the real server’s IP is never disclosed and only the proxy server knows where it is. Also let’s pretend msg_with_payload is an attempted RCE attack payload.

Server

Here’s server.py, downloadable here

#!/usr/bin/python

# https://www.binarytides.com/code-telnet-client-sockets-python/

import sys
import socket
import select
import string

if len(sys.argv) != 3:
    print "Usage: server.py [server_ip] [server_port]"
    print "  Sets up a basic 1 to 1 chat server"
    print "  For localhost connections, set server_ip to 0.0.0.0"
    exit()

addr = sys.argv[1]
port = int(sys.argv[2])

s = socket.socket()
s.bind((addr,port))
s.listen(1)

print "[+] server live at %s:%d" % (addr, port)

# waits for connection (holds/blocks script)
client_socket,addr = s.accept()

print "[+] %s connected, binded to port %d" % (addr[0], addr[1])

while 1:
    sockets = [sys.stdin, client_socket] # diantara 2 stream ini
    read, write, error = select.select(sockets, [], []) # yg mana dulu yg "diinput"

    for sock in read:
        if sock == client_socket: # kalo dari client ada ngomong
            try:
                msg = client_socket.recv(2048) # print apa yg client bilang
                if len(msg):
                    print "client> "+msg
            except Exception as e:
                print e
                print "[-] %s disconnected" % addr[0]
                exit()
        else: # kalo kita yg ngomong
            msg = sys.stdin.readline() # kasi tau kita ngomong apa
            msg = string.rstrip(msg)
            try:
                client_socket.send(msg)
            except Exception as e:
                print e
                print "[-] %s disconnected" % addr[0]
                exit()

It’s simpler because we only have to connect to 1 client, so no need for holding an array of active clients, broadcasting, etc.

Client

Here’s client.py, downloadable here

#!/usr/bin/python

# https://www.binarytides.com/code-telnet-client-sockets-python/

import sys
import socket
import select
import string

if len(sys.argv) != 3:
    print "Usage: client.py [server_ip] [server_port]"
    print "  Connects to a basic 1 to 1 chat server"
    print "  For localhost connections, set server_ip to 0.0.0.0"
    exit()

dest = sys.argv[1]
port = int(sys.argv[2])

srv = socket.socket()
try:
	srv.connect((dest,port))
except Exception as e:
	print "[-] Server down"
	exit()

while 1:
    sockets = [sys.stdin, srv]
    read, write, error = select.select(sockets, [], [])

    for sock in read:
        if sock == srv: # kalo dari server ada ngomong
            try:
                msg = srv.recv(2048)
                if len(msg):
                    print "server> "+msg
            except:
                print "[-] server down"
                exit()
        else: # kalo kita yg ngomong
            msg = sys.stdin.readline()
            msg = string.rstrip(msg)
            try:
                srv.send(msg)
            except:
                print "[-] server down"
                exit()

We can see that client.py is really similar to server.py outside of the client doing the connecting and the server doing the listening.

Proxy

Here’s proxy.py, downloadable here

#!/usr/bin/python

# https://www.binarytides.com/code-telnet-client-sockets-python/

import sys
import socket
import select
import string

if len(sys.argv) != 5:
    print "Usage: proxy.py [proxy_ip] [proxy_port] [server_ip] [server_port]"
    print "  Sets up a proxy server at proxy_ip that forwards connections to server_ip"
    print "  Only 1 connection at a time"
    print "  For localhost connections, set server_ip to 0.0.0.0"
    exit()

myip = sys.argv[1]
myport = int(sys.argv[2])

srvip = sys.argv[3]
srvport = int(sys.argv[4])

srv_socket = socket.socket()
try:
    srv_socket.connect((srvip, srvport))
except:
    print "[-] Server down"
    exit()

print "[+] connected to tcp server at %s:%d" % (srvip, srvport)

# nunggu koneksi dri client
mysocket = socket.socket()
mysocket.bind((myip, myport))
mysocket.listen(1)

client_socket,addr = mysocket.accept()

print "[+] client %s connected, now connecting to chat server %s" % (addr[0], srvip)

def filter_msg(msg):
    if msg == "msg_with_payload":
        print("[!] client just attempted an attack!")
        msg = "filtered_msg"
    return msg

# skrg kita punya 2 socket idup
# client_socket ama srv_socket
while 1:
    sockets = [client_socket, srv_socket]
    read, write, error = select.select(sockets, [], [])

    for sock in read:
        if sock == client_socket: # client mau send ke srv
            try:
                msg = client_socket.recv(2048)
                msg = filter_msg(msg)
                if len(msg):
                    print "[ ] client> "+msg
                    srv_socket.send(msg)
            except:
                print "[-] couldn't forward message to server"
                exit()
        else: # srv balikin ke client
            try:
                msg = srv_socket.recv(2048)
                if len(msg):
                    print "[ ] server> "+msg
                    client_socket.send(msg)
            except:
                print "[-] couldn't forward message to client"
                exit()

Here’s the fun part, we can see that the message filtering happens here, so we can upgrade our filters and other security measures right here and also simultaneously have more layers of security over at our real server.