Skip to content

Instantly share code, notes, and snippets.

@cybiere
Created May 12, 2020 16:18
Show Gist options
  • Save cybiere/abe5caa3a7504bfd733eb2e5eb829fb1 to your computer and use it in GitHub Desktop.
Save cybiere/abe5caa3a7504bfd733eb2e5eb829fb1 to your computer and use it in GitHub Desktop.
This is a basic python SOCK5 server which forwards the traffic through a SSH tunnel
#!/usr/bin/env python3
#This is https://github.com/rushter 's python socks server
#Tweaked by https://github.com/cybiere to forward to a SSH server
#via Fabric https://github.com/fabric/fabric
#to act as ssh -D
import logging
import select
import socket
import struct
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
import fabric
logging.basicConfig(level=logging.INFO)
SOCKS_VERSION = 5
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
#SPECIFY THE TARGET HOST HERE
gateway = fabric.Connection(host='10.0.1.101', user='user', connect_kwargs={"password":"resu"})
gateway.open()
class SocksProxy(StreamRequestHandler):
def handle(self):
logging.info('Accepting connection from %s:%s' % self.client_address)
# greeting header
# read and unpack 2 bytes from a client
header = self.connection.recv(2)
version, nmethods = struct.unpack("!BB", header)
# socks 5
assert version == SOCKS_VERSION
assert nmethods > 0
# get available methods
methods = self.get_available_methods(nmethods)
# accept only NO AUTH auth
if 0 not in set(methods):
# close connection
self.server.close_request(self.request)
return
# send welcome message
self.connection.sendall(struct.pack("!BB", SOCKS_VERSION, 0))
# request
version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
assert version == SOCKS_VERSION
if address_type == 1: # IPv4
address = socket.inet_ntoa(self.connection.recv(4))
elif address_type == 3: # Domain name
domain_length = ord(self.connection.recv(1)[0])
address = self.connection.recv(domain_length)
port = struct.unpack('!H', self.connection.recv(2))[0]
# reply
try:
if cmd == 1: # CONNECT
remote = gateway.transport.open_channel(
kind="direct-tcpip",
dest_addr=(address,port),
src_addr=("",0)
)
#remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#remote.connect((address, port))
# bind_address = remote.getsockname()
logging.info('Connected to %s %s' % (address, port))
else:
self.server.close_request(self.request)
#addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
#port = bind_address[1]
addr = struct.unpack("!I", socket.inet_aton(address))[0]
port = int(port)
reply = struct.pack("!BBBBIH", SOCKS_VERSION, 0, 0, address_type,
addr, port)
except Exception as err:
logging.error(err)
# return connection refused error
reply = self.generate_failed_reply(address_type, 5)
self.connection.sendall(reply)
# establish data exchange
if reply[1] == 0 and cmd == 1:
self.exchange_loop(self.connection, remote)
self.server.close_request(self.request)
def get_available_methods(self, n):
methods = []
for i in range(n):
methods.append(ord(self.connection.recv(1)))
return methods
def generate_failed_reply(self, address_type, error_number):
return struct.pack("!BBBBIH", SOCKS_VERSION, error_number, 0, address_type, 0, 0)
def exchange_loop(self, client, remote):
while True:
# wait until client or remote is available for read
r, w, e = select.select([client, remote], [], [])
if client in r:
data = client.recv(4096)
if remote.send(data) <= 0:
break
if remote in r:
data = remote.recv(4096)
if client.send(data) <= 0:
break
if __name__ == '__main__':
with ThreadingTCPServer(('127.0.0.1', 1080), SocksProxy) as server:
server.serve_forever()
gateway.close()
@AliAkhtari78
Copy link

How can I add basic authentication to this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment