mgeeky-Penetration-Testing-.../networks/tcpproxy.py

265 lines
8.5 KiB
Python

#!/usr/bin/python
import sys
import socket
import argparse
import threading
config = {
'debug': False,
'verbose': False,
'timeout' : 5.0,
}
# =========================================================
# CUSTOM ANALYSIS, FUZZING, INTERCEPTION ROUTINES.
def requestHandler(buff):
'''
Modify any requests destined for the REMOTE host service.
'''
return buff
def responseHandler(buff):
'''
Modify any responses destined for the LOCAL host service.
'''
return buff
# =========================================================
class Logger:
@staticmethod
def _out(x):
if config['debug'] or config['verbose']:
sys.stderr.write(x + '\n')
@staticmethod
def dbg(x):
if config['debug']:
sys.stderr.write('[dbg] ' + x + '\n')
@staticmethod
def out(x):
Logger._out('[.] ' + x)
@staticmethod
def info(x):
Logger._out('[?] ' + x)
@staticmethod
def err(x, fatal = False):
Logger._out('[!] ' + x)
if fatal: sys.exit(-1)
@staticmethod
def fail(x, fatal = False):
Logger._out('[-] ' + x)
if fatal: sys.exit(-1)
@staticmethod
def ok(x):
Logger._out('[+] ' + x)
def hexdump(src, length = 16):
result = []
digits = 4 if isinstance(src, unicode) else 2
num = len(src)
for i in range(0, num, length):
s = src[i:i+length]
hexa = b' '.join(['%0*X' % (digits, ord(x)) for x in s])
text = b''.join([x if 0x20 <= ord(x) < 0x7f else b'.' for x in s])
result.append(b'%04x | %-*s | %s' % (i, length * (digits + 1), hexa, text))
return str(b'\n'.join(result))
def recvFrom(sock):
'''
Simple recvAll based on timeout exception.
'''
buff = ''
sock.settimeout(config['timeout'])
try:
while True:
data = sock.recv(4096)
if not data: break
buff += data
except:
pass
return buff
def proxyHandler(clientSock, remoteHost, remotePort, recvFirst):
Logger.dbg('Connecting to REMOTE service: {}:{}'.format(remoteHost, remotePort))
try:
remoteSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remoteSock.settimeout(config['timeout'])
remoteSock.connect((remoteHost, remotePort))
Logger.dbg('Connected.')
except Exception as e:
Logger.err('TCP Proxy was unable to connect to REMOTE service: {}:{}'.format(
remoteHost, remotePort), fatal = True
)
if recvFirst:
remoteBuff = recvFrom(remoteSock)
Logger.info('[<==] Received {} bytes from REMOTE service.'.format(len(remoteBuff)))
Logger.dbg('Remote service Recv buff BEFORE responseHandler:\n' + hexdump(remoteBuff))
remoteBuffOrig = remoteBuff
remoteBuff = responseHandler(remoteBuff)
if remoteBuff != remoteBuffOrig:
Logger.dbg('Buffer to be sent to LOCAL service modified. Lengths: {} -> {}'.format(
len(remoteBuffOrig, remoteBuff)))
Logger.dbg('Remote service Recv buff AFTER responseHandler:\n' + hexdump(remoteBuff))
if len(remoteBuff):
Logger.info('[<==] Sending {} bytes to LOCAL service.'.format(len(remoteBuff)))
clientSock.send(remoteBuff)
# Send & Receive / Proxy loop
while True:
# LOCAL part
localBuff = recvFrom(clientSock)
if len(localBuff):
Logger.info('[==>] Received {} bytes from LOCAL service.'.format(len(localBuff)))
Logger.dbg('Local service Recv buff:\n' + hexdump(localBuff))
localBuffOrig = localBuff
localBuff = requestHandler(localBuff)
if localBuff != localBuffOrig:
Logger.dbg('Buffer to be sent to REMOTE service modified. Lengths: {} -> {}'.format(
len(localBuffOrig, localBuff)))
Logger.dbg('Local service Recv buff AFTER requestHandler:\n' + hexdump(localBuff))
remoteSock.send(localBuff)
Logger.info('[==>] Sent to REMOTE service.')
# REMOTE part
remoteBuff = recvFrom(remoteSock)
if len(remoteBuff):
Logger.info('[<==] Received {} bytes from REMOTE service.'.format(len(remoteBuff)))
Logger.dbg('Remote service Recv buff:\n' + hexdump(remoteBuff))
remoteBuffOrig = remoteBuff
remoteBuff = responseHandler(remoteBuff)
if remoteBuff != remoteBuffOrig:
Logger.dbg('Buffer to be sent to LOCAL service modified. Lengths: {} -> {}'.format(
len(remoteBuffOrig, remoteBuff)))
Logger.dbg('Remote service Recv buff AFTER responseHandler:\n' + hexdump(remoteBuff))
clientSock.send(remoteBuff)
Logger.info('[<==] Sent to LOCAL service.')
if not len(localBuff) or not len(remoteBuff):
clientSock.close()
remoteSock.close()
Logger.info('No more data. Closing connections.')
break
def serverLoop(localHost, localPort, remoteHost, remotePort, receiveFirst):
serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
serv.bind((localHost, localPort))
Logger.ok('TCP Proxy listening on: {}:{}'.format(localHost, localPort))
serv.listen(5)
except Exception as e:
Logger.err('TCP Proxy server was unable to bound to {}:{}'.format(localHost, localPort), fatal = True)
while True:
clientSock, addr = serv.accept()
Logger.info('[==>] Received incoming connection from: {}:{}'.format(addr[0], addr[1]))
proxyThread = threading.Thread(
target = proxyHandler,
args = (
clientSock,
remoteHost,
remotePort,
receiveFirst
)
)
proxyThread.start()
def processOpts(argv):
global config
usageStr = '''
tcpproxy.py [options] <LOCAL> <REMOTE>
Example:
tcpproxy.py 127.0.0.1:9000 192.168.56.102:9000
'''
parser = argparse.ArgumentParser(prog = argv[0], usage = usageStr)
parser.add_argument('localhost', metavar='LOCAL', type=str,
help = 'Local service to proxy (host:port)')
parser.add_argument('remotehost', metavar='REMOTE', type=str,
help = 'Remote service to proxy to (host:port)')
parser.add_argument('-r', '--recvfirst', dest='recvfirst', action='store_true', default = False,
help='Make the proxy first receive something, than respond.')
parser.add_argument('-t', '--timeout', metavar='timeout', dest='timeout', default = config['timeout'],
help='Specifies service connect & I/O timeout. Default: {}.'.format(config['timeout']))
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
help='Show verbose output.')
parser.add_argument('-d', '--debug', dest='debug', action='store_true',
help='Show more verbose, debugging output.')
if len(sys.argv[1:]) < 2:
parser.print_help()
sys.exit(0)
args = parser.parse_args()
if args.debug:
config['debug'] = args.debug
if args.verbose:
config['verbose'] = args.verbose
config['timeout'] = float(args.timeout)
Logger.dbg('Timeout set to: {} seconds.'.format(args.timeout))
return (args.localhost, args.remotehost, args.recvfirst)
def main():
local, remote, recvfirst = processOpts(sys.argv)
localHost, localPort = local.split(':')
remoteHost, remotePort = remote.split(':')
try:
localPort = int(localPort)
if localPort < 0 or localPort > 65535:
raise ValueError
except ValueError:
Logger.err('Invalid LOCAL port specified.', fatal = True)
try:
remotePort = int(remotePort)
if remotePort < 0 or remotePort > 65535:
raise ValueError
except ValueError:
Logger.err('Invalid LOCAL port specified.', fatal = True)
Logger.info('Proxying: {}:{} => {}:{}'.format(
localHost, localPort, remoteHost, remotePort
))
serverLoop(localHost, localPort, remoteHost, remotePort, recvfirst)
if __name__ == '__main__':
main()