0a5818e
#    Proxyfuzz - On the fly TCP and UDP network fuzzer
0a5818e
#    Copyright (C) 2011 Rodrigo Marcos
0a5818e
#
0a5818e
#    This program is free software: you can redistribute it and/or modify
0a5818e
#    it under the terms of the GNU General Public License as published by
0a5818e
#    the Free Software Foundation, either version 3 of the License, or
0a5818e
#    (at your option) any later version.
0a5818e
#
0a5818e
#    This program is distributed in the hope that it will be useful,
0a5818e
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
0a5818e
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0a5818e
#    GNU General Public License for more details.
0a5818e
#
0a5818e
#    You should have received a copy of the GNU General Public License
0a5818e
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0a5818e
#
0a5818e
#    Proxyfuzz: a TCP and UDP proxy man-in-the-middle fuzzer
0a5818e
#    v 0.1
0a5818e
#    By Rodrigo Marcos
0a5818e
#    http://www.secforce.co.uk
0a5818e
0a5818e
from twisted.protocols import portforward
0a5818e
from twisted.internet.protocol import DatagramProtocol
0a5818e
from twisted.internet import reactor
0a5818e
import getopt, sys
0a5818e
from random import randint
0a5818e
0a5818e
0a5818e
# UDP Proxy stuff
0a5818e
0a5818e
class Client(DatagramProtocol):
0a5818e
	def __init__(self, server, host, port):
0a5818e
		self.server = server
0a5818e
		self.host = host
0a5818e
		self.port = port
0a5818e
    
0a5818e
	def startProtocol(self):
0a5818e
		self.transport.connect(desthost, destport)
0a5818e
    
0a5818e
	def datagramReceived(self, data, (host, port)):
0a5818e
		global verbose
0a5818e
		global notuntil
0a5818e
		global request
0a5818e
		global testclient
0a5818e
	
0a5818e
		if testclient:
0a5818e
			if request < notuntil:
0a5818e
				request = request + 1
0a5818e
			else:
0a5818e
				data = fuzz(data)
0a5818e
		if verbose:
0a5818e
			print "Server ------> Client"
0a5818e
			print "%r" % data
0a5818e
		
0a5818e
	        self.server.transport.write(data, (self.host, self.port))
0a5818e
 
0a5818e
class Server(DatagramProtocol):
0a5818e
	client = None
0a5818e
    
0a5818e
	def datagramReceived(self, data, (host, port)):
0a5818e
		if not self.client or self.client.host != host or self.client.port != port:
0a5818e
			self.client = Client(self, host, port)
0a5818e
			reactor.listenUDP(0, self.client)
0a5818e
0a5818e
		global verbose
0a5818e
		global notuntil
0a5818e
		global request
0a5818e
		global testserver
0a5818e
		
0a5818e
		if testserver:
0a5818e
			if request < notuntil:
0a5818e
				request = request + 1
0a5818e
			else:
0a5818e
				data = fuzz(data)
0a5818e
		if verbose:
0a5818e
			print "Client ------> Server"
0a5818e
			print "%r" % data 
0a5818e
		self.client.transport.write(data, (desthost, destport))
0a5818e
 
0a5818e
# TCP proxy stuff
0a5818e
0a5818e
def server_dataReceived(self, data):
0a5818e
	global verbose
0a5818e
	global notuntil
0a5818e
	global request
0a5818e
	global testserver
0a5818e
	
0a5818e
	if testserver:
0a5818e
		if request < notuntil:
0a5818e
			request = request + 1
0a5818e
		else:
0a5818e
			data = fuzz(data)
0a5818e
0a5818e
	if verbose:
0a5818e
		print "Client ------> server"
0a5818e
		print "%r" % data
0a5818e
		
0a5818e
	portforward.Proxy.dataReceived(self, data)
0a5818e
0a5818e
portforward.ProxyServer.dataReceived = server_dataReceived
0a5818e
0a5818e
def client_dataReceived(self, data):
0a5818e
	global verbose
0a5818e
	global notuntil
0a5818e
	global request
0a5818e
	global testclient
0a5818e
	
0a5818e
	if testclient:
0a5818e
		if request < notuntil:
0a5818e
			request = request + 1
0a5818e
		else:
0a5818e
			data = fuzz(data)
0a5818e
	if verbose:
0a5818e
		print "Server ------> Client"
0a5818e
		print "%r" % data
0a5818e
	
0a5818e
	portforward.Proxy.dataReceived(self, data)
0a5818e
0a5818e
portforward.ProxyClient.dataReceived = client_dataReceived
0a5818e
0a5818e
       
0a5818e
overflowstrings = ["A" * 255, "A" * 256, "A" * 257, "A" * 420, "A" * 511, "A" * 512, "A" * 1023, "A" * 1024, "A" * 2047, "A" * 2048, "A" * 4096, "A" * 4097, "A" * 5000, "A" * 10000, "A" * 20000, "A" * 32762, "A" * 32763, "A" * 32764, "A" * 32765, "A" * 32766, "A" * 32767, "A" * 32768, "A" * 65534, "A" * 65535, "A" * 65536, "%x" * 1024, "%n" * 1025 , "%s" * 2048, "%s%n%x%d" * 5000, "%s" * 30000, "%s" * 40000, "%.1024d", "%.2048d", "%.4096d", "%.8200d", "%99999999999s", "%99999999999d", "%99999999999x", "%99999999999n", "%99999999999s" * 1000, "%99999999999d" * 1000, "%99999999999x" * 1000, "%99999999999n" * 1000, "%08x" * 100, "%%20s" * 1000,"%%20x" * 1000,"%%20n" * 1000,"%%20d" * 1000, "%#0123456x%08x%x%s%p%n%d%o%u%c%h%l%q%j%z%Z%t%i%e%g%f%a%C%S%08x%%#0123456x%%x%%s%%p%%n%%d%%o%%u%%c%%h%%l%%q%%j%%z%%Z%%t%%i%%e%%g%%f%%a%%C%%S%%08x"]
0a5818e
	
0a5818e
def bitflipping(data):
0a5818e
	l = len(data)
0a5818e
	n = int(l*7/100) # 7% of the bytes to be modified
0a5818e
	
0a5818e
	for i in range(0,n): # We change the bytes
0a5818e
		r = randint(0,l-1)
0a5818e
		data = data[0:r] + chr(randint(0,255)) + data[r+1:]
0a5818e
	return data
0a5818e
0a5818e
def bofinjection(data):
0a5818e
	l = len(data)
0a5818e
	r = randint(0,len(overflowstrings)-1)
0a5818e
	data = data[0:r] + overflowstrings[r] + data[r-l:]
0a5818e
	return data
0a5818e
0a5818e
def fuzz(data):
0a5818e
0a5818e
	r = randint(0,5)
0a5818e
	if r==0:
0a5818e
		data = bitflipping(data)
0a5818e
0a5818e
	r = randint(0,5)
0a5818e
	if r==0:
0a5818e
		data = bofinjection(data)
0a5818e
	return data
0a5818e
	
0a5818e
def startudpproxy():
0a5818e
	reactor.listenUDP(localport, Server())
0a5818e
	reactor.run()
0a5818e
	
0a5818e
def starttcpproxy():
0a5818e
	reactor.listenTCP(localport, portforward.ProxyFactory(desthost, destport))
0a5818e
	reactor.run()
0a5818e
0a5818e
def usage():
0a5818e
	#print "###############################################################"
0a5818e
	print
0a5818e
	print "ProxyFuzz 0.1, Simple fuzzing proxy by Rodrigo Marcos"
0a5818e
	print "http://www.secforce.co.uk"
0a5818e
	print
0a5818e
	print "usage():"
0a5818e
	#print
0a5818e
	print "python proxyfuzz -l <localport> -r <remotehost> -p <remoteport> [options]"
0a5818e
	print
0a5818e
	print " [options]"
0a5818e
	print "		-c: Fuzz only client side (both otherwise)"
0a5818e
	print "		-s: Fuzz only server side (both otherwise)"
0a5818e
	print "		-w: Number of requests to send before start fuzzing"
0a5818e
	print "		-u: UDP protocol (otherwise TCP is used)"
0a5818e
	print "		-v: Verbose (outputs network traffic)"
0a5818e
	print "		-h: Help page"
0a5818e
0a5818e
0a5818e
verbose = False
0a5818e
notuntil = 0
0a5818e
request = 0
0a5818e
proto = "tcp"
0a5818e
localport = 0
0a5818e
desthost = ""
0a5818e
destport = 0
0a5818e
testclient = 1
0a5818e
testserver = 1
0a5818e
def main():
0a5818e
	global verbose
0a5818e
	global notuntil
0a5818e
	global proto
0a5818e
	global localport
0a5818e
	global desthost
0a5818e
	global destport
0a5818e
	global testclient
0a5818e
	global testserver
0a5818e
	
0a5818e
	try:
0a5818e
		opts, args = getopt.getopt(sys.argv[1:], "uvhcsl:r:p:w:", ["help"])
0a5818e
	except getopt.GetoptError:
0a5818e
		usage()
0a5818e
		sys.exit(2)
0a5818e
	try:
0a5818e
		for o, a in opts:
0a5818e
			if o in ("-h", "--help"):
0a5818e
				usage()
0a5818e
				sys.exit()
0a5818e
			if o == "-l":
0a5818e
				localport=int(a)
0a5818e
			if o == "-r":
0a5818e
				desthost=a
0a5818e
			if o == "-p":
0a5818e
				destport=int(a)
0a5818e
			if o == "-v":
0a5818e
				verbose = True
0a5818e
			if o == "-w":
0a5818e
				notuntil=int(a)
0a5818e
			if o == "-u":
0a5818e
				proto="udp"
0a5818e
			if o == "-c": # Only client
0a5818e
				testserver=0
0a5818e
			if o == "-s": # Only server
0a5818e
				testclient=0
0a5818e
				
0a5818e
				
0a5818e
	except:
0a5818e
		usage()
0a5818e
		sys.exit(2)
0a5818e
		
0a5818e
	if testserver==0 and testclient==0:
0a5818e
		usage()
0a5818e
		sys.exit(2)
0a5818e
	elif localport==0 or desthost=="" or destport==0:
0a5818e
		usage()
0a5818e
		sys.exit(2)
0a5818e
	else:
0a5818e
		if proto=="tcp":
0a5818e
			starttcpproxy()
0a5818e
		else: # UDP
0a5818e
			startudpproxy()
0a5818e
0a5818e
if __name__ == "__main__":
0a5818e
    main()
0a5818e
    
0a5818e