#
# TAOF, the general purpose TCP fuzzer.
# Copyright (C) 2007 Rodrigo Marcos
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#Python libraries
import time
import socket
import timeoutsocket
import sys
from struct import pack
#Taof libraries
import fuzzutils
#Third party libraries
import gtk
#Fuzzing signatures
overflowstrings = ["A" * 33, "A" * 128, "A" * 240, "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]
formatstrings = ["%s" * 4, "%s" * 8, "%s" * 15, "%s" * 30, "%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"]
integerstrings = [-1, 0, 1, 127, 128, 255, 256, 32767, 32768, 65535, 65536, 2097151, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741823, 1073741824, 2147483647, 2147483648, 4294967295]
integerstrings_little_endian = []
for i in integerstrings:
integerstrings_little_endian.append(pack('<I',i))
integerstrings_big_endian = []
for i in integerstrings:
integerstrings_big_endian.append(pack('>I',i))
#############################################################################
#
# Fuzzer class
#
#############################################################################
class fuzzer:
def wait(self, seconds):
t=time.time()
while 1==1:
if time.time()>t + seconds:
break
else:
while gtk.events_pending(): # Run pending events
gtk.main_iteration_do(False)
def send_as_is(self):
#This function sends "as_is" requests
for requestid in range(0, fuzzutils.context["xmllog"].get_number_requests(fuzzutils.context["xmldoc"])):
myrequest = fuzzutils.context["xmllog"].get_request(fuzzutils.context["xmldoc"],requestid)
if myrequest.attributes["as_is"].value == "1":
try:
current_request = fuzzutils.context["logger"].read_request(requestid+1)
self.remoteserver.send(current_request)
self.recv(fuzzutils.context["RECV_BUFFER_SIZE"])
except:
# print str(sys.exc_info()[0])
return "0"
return "1"
def fuzz_loop(self, signatures, first_part, last_part, isnumber, checklen, lvalue, middle_part, len_position, lformat):
#TO DO non dictionary attacks:
# Learn the result of a bad request and compare the result to it instead of dumping all the results on the debugging file
# Threading
for sg in signatures:
#time.sleep(fuzzutils.context["wait"]) # Wait for each request
fuzzutils.context["currentsignature"] = fuzzutils.context["currentsignature"] + 1
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),'*')#Adds a '*' on the fuzzing dialog
while gtk.events_pending(): # Run pending events
gtk.main_iteration_do(False)
if not isnumber:
sg=sg.strip() #Removing the new line if this is a dictionay attack, otherwise... there shouldn't be a newline
try:
if checklen=='1' and not isnumber:
injectedlen = lvalue + len(sg)
if lformat == '1': # ASCII
injectedlen = str(injectedlen)
elif lformat == '2': # Little endian
injectedlen = pack('<l',injectedlen)
else: # Big endian
injectedlen = pack('>l',injectedlen)
if len_position == 0: # PREPEND value
if isnumber:
fuzzed_request = first_part + injectedlen + middle_part + str(sg) + last_part
else:
fuzzed_request = first_part + injectedlen + middle_part + sg + last_part
else: # POSTPEND value
if isnumber:
fuzzed_request = first_part + str(sg) + middle_part + injectedlen + last_part
else:
fuzzed_request = first_part + sg + middle_part + injectedlen + last_part
else:
if isnumber:
fuzzed_request = first_part + str(sg) + last_part
else:
fuzzed_request = first_part + sg + last_part
if fuzzutils.context["debug"]:
if isnumber:
debugstring = "\n*************************<REQUEST>***********************\n"
else:
debugstring = "\n*************************<REQUEST>*********************** - " + str(len(sg)) + " characters\n"
self.file_object.write(debugstring)
self.file_object.write(fuzzed_request)
self.file_object.write("\n*************************<REQUEST/>***********************\n")
if fuzzutils.context["proto"]=="tcp": # TCP
self.remoteserver.send(fuzzed_request) # Send...
results = self.remoteserver.recv(fuzzutils.context["RECV_BUFFER_SIZE"]) # and receive...
else: # UDP
self.remoteserver.sendto(fuzzed_request, (fuzzutils.context["dest_host"], int(fuzzutils.context["dest_port"]))) # Send...
results, addr = self.remoteserver.recvfrom(fuzzutils.context["RECV_BUFFER_SIZE"]) # and receive...
if results=="": # This usually means that the remote server closed the connection
#Although we run the risk of sending things twice, otherwise we might skip some vulnerabilities
res = self.reconnect() # Taof first try to reconnect and repeat the request again
#Let's handle the reconnection.
if res=='1': # Success reconnecting
if fuzzutils.context["proto"]=="tcp": # TCP
self.remoteserver.send(fuzzed_request) # Send...
results = self.remoteserver.recv(fuzzutils.context["RECV_BUFFER_SIZE"]) # and receive...
else: # UDP
self.remoteserver.sendto(fuzzed_request, (fuzzutils.context["dest_host"], int(fuzzutils.context["dest_port"]))) # Send...
results, addr = self.remoteserver.recvfrom(fuzzutils.context["RECV_BUFFER_SIZE"]) # and receive...
#self.remoteserver.send(fuzzed_request)
#results = self.remoteserver.recv(fuzzutils.context["RECV_BUFFER_SIZE"])
if fuzzutils.context["debug"]:
self.file_object.write("\n+++++++++++++++++++++++++<RESPONSE>+++++++++++++++++++++++++\n")
self.file_object.write(results)
self.file_object.write("\n+++++++++++++++++++++++++<RESPONSE/>+++++++++++++++++++++++++\n")
elif res=='-1': # This might be good (connection issue, the service might have died)
if fuzzutils.context["debug"]:
if isnumber:
debugstring = "\n*** Check this out carefully. This request killed the service.\n"
else:
debugstring = "\n*** Check this out carefully. This request killed the service. Request - " + str(len(sg)) + " characters\n"
self.file_object.write(debugstring)
self.file_object.write("\n*************************<BUFFER>***********************\n")
self.file_object.write(fuzzed_request)
self.file_object.write("\n*************************<BUFFER/>***********************\n")
self.file_object.write("\n[*] I have to give up. I can not connect to the server" )
fuzzutils.context["stopfuzzing"]=2
else: # everything went ok
if fuzzutils.context["debug"]:
self.file_object.write("\n+++++++++++++++++++++++++<RESPONSE>+++++++++++++++++++++++++\n")
self.file_object.write(results)
self.file_object.write("\n+++++++++++++++++++++++++<RESPONSE/>+++++++++++++++++++++++++\n")
except socket.error, msg:
if fuzzutils.context["debug"]:
self.file_object.write("\n----------------------------<SOCKET EXCEPTION>-------------------------\n")
logtemp = "Socket.error: " + str(msg)
self.file_object.write(logtemp)
self.file_object.write("\n----------------------------<SOCKET EXCEPTION/>-------------------------\n")
res = self.reconnect()
if res=='-1': # This might be good (connection issue, the service might have died)
if fuzzutils.context["debug"]:
if isnumber:
debugstring = "\n*** Check this out carefully. This request killed the service.\n"
else:
debugstring = "\n*** Check this out carefully. This request killed the service. Request - " + str(len(sg)) + " characters\n"
self.file_object.write(debugstring)
self.file_object.write("\n*************************<BUFFER>***********************\n")
self.file_object.write(fuzzed_request)
self.file_object.write("\n*************************<BUFFER/>***********************\n")
self.file_object.write("\n[*] I have to give up. I can not connect to the server" )
fuzzutils.context["stopfuzzing"]=2
except:
if fuzzutils.context["debug"]:
self.file_object.write("\n----------------------------<EXCEPTION>-------------------------\n")
self.file_object.write(str(sys.exc_info()[1]) )#+ " - " + sys.exc_info()[1])
self.file_object.write("\n----------------------------<EXCEPTION/>-------------------------\n")
res = self.reconnect()
if res=='-1': # This might be good (connection issue, the service might have died)
if fuzzutils.context["debug"]:
if isnumber:
debugstring = "\n*** Check this out carefully. This request killed the service.\n"
else:
debugstring = "\n*** Check this out carefully. This request killed the service. Request - " + str(len(sg)) + " characters\n"
self.file_object.write(debugstring)
self.file_object.write("\n*************************<BUFFER>***********************\n")
self.file_object.write(fuzzed_request)
self.file_object.write("\n*************************<BUFFER/>***********************\n")
self.file_object.write("\n[*] I have to give up. I can not connect to the server" )
fuzzutils.context["stopfuzzing"]=2
def reconnect(self):
#This function reconnects to the remote server. If not possible the first time it tries again.
#Returns '1' on success and '-1' if reconnection was not possible
try:
if fuzzutils.context["proto"]=="tcp": # TCP
self.remoteserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else: #UDP
self.remoteserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
result = self.send_as_is()
return '1'
except:
pass
connected = 0
for i in range(0,2):
try:
self.remoteserver.connect((fuzzutils.context["dest_host"], int(fuzzutils.context["dest_port"])))
connected=1
except socket.error, desc:
#print desc
if desc[0]==106:
connected=1 # Already connected
else:
logtemp = "\n[*] It was not possible to connect to " + fuzzutils.context["dest_host"] + ":" + fuzzutils.context["dest_port"] + ". It might be down. Retrying now..."
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
#time.sleep(fuzzutils.context["wait_reconnection"]) # Let's wait a little bit
except:
#print str(sys.exc_info()[1])
logtemp = "\n[*] It was not possible to connect to " + fuzzutils.context["dest_host"] + ":" + fuzzutils.context["dest_port"] + ". It might be down. Retrying now..."
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
self.wait(fuzzutils.context["wait_reconnection"]) # Let's wait a little bit
if connected==1:
break
if connected==0: # Ooops, we could not connect to the server. The service might have died
logtemp = "\n[*] I could not connect to the server. I might have killed the service (which is good!)."
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
return '-1'
result = self.send_as_is()
# if fuzzutils.context["debug"]:
# logtemp = "Authentication result:" + result + "\n"
# self.file_object.write(logtemp)
return '1'
def run(self):
# This is the main loop for fuzzing
if fuzzutils.context["debug"]:
self.file_object = open("debugging" , "w")
logtemp = "Starting fuzzing session against " + fuzzutils.context["dest_host"] + ":" + fuzzutils.context["dest_port"] + " - " + time.strftime("%H:%M:%S")##########################
self.file_object.write(logtemp)
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),"Check 'debugging' file for further information" + '\n\n')
#initialization
res = self.reconnect() # it establishes the connection to the server
if res!='1':
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),"\nCould not connect to the server.\n")
if fuzzutils.context["debug"]:
self.file_object.write("Could not connect to the server.\n")
return "1"
#loops though all the requests
for requestid in range(0, fuzzutils.context["xmllog"].get_number_requests(fuzzutils.context["xmldoc"])):
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
current_request = fuzzutils.context["logger"].read_request(requestid+1) # we read the original request
logtemp = "Fuzzing request: " + str(requestid)
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
logtemp = " Number of fuzzing points: " + str(fuzzutils.context["xmllog"].get_number_fuzz(fuzzutils.context["xmldoc"], requestid))
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
if fuzzutils.context["debug"]:
logtemp = "Fuzzing request: " + str(requestid) + "\n"
self.file_object.write(logtemp)
logtemp = " Number of fuzzing points: " + str(fuzzutils.context["xmllog"].get_number_fuzz(fuzzutils.context["xmldoc"],requestid)) + "\n"
self.file_object.write(logtemp)
#It goes through each of the fuzzing points on the current request
for fuzzid in range (0, fuzzutils.context["xmllog"].get_number_fuzz(fuzzutils.context["xmldoc"], requestid)):
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
fuzzutils.context["currentsignature"] = 0
ffrom, fto = fuzzutils.context["xmllog"].get_from_to(fuzzutils.context["xmldoc"], requestid, fuzzid)
#it gest the boundaries of the fuzzing point
checklen, lfrom, lto, lvalue, lformat = fuzzutils.context["xmllog"].get_len(fuzzutils.context["xmldoc"], requestid, fuzzid)
#It gets all the necessary parameters for variable lenght calculations
print "Testing fuzzing point ", fuzzid, ": (", ffrom, " - ", fto, ")"
if fuzzutils.context["debug"]:
logtemp = "Testing fuzzing point " + str(fuzzid) + ": (" + ffrom + " - " + fto + ")"
self.file_object.write(logtemp)
ffrom = int(ffrom)
fto = int(fto)
if checklen=='1': # This means that we need to include a variable lenght with the request
lfrom = int(lfrom)
lto = int(lto)
lvalue = int(lvalue)
#in order to inject the length in the correct place, we calculate the location:
if lto<=ffrom:
# the lenght value is located BEFORE the injection point
first_part = current_request[:lfrom]
middle_part = current_request[lto:ffrom]
last_part = current_request[fto:]
len_position = 0
else: # We trust the value entry (we shouldn't!). To do, check that "lfrom>=to"
# the lenght value is located AFTER the injection point
first_part = current_request[:ffrom]
middle_part = current_request[fto:lfrom]
last_part = current_request[lto:]
len_position = 1
else: # No lenght has to be included
first_part = current_request[:ffrom]
last_part = current_request[fto:]
middle_part = ''
len_position = -1
#fuzzing buffer overflows
if fuzzutils.context["xmllog"].fuzz_bo(fuzzutils.context["xmldoc"], requestid, fuzzid)=='1':
logtemp = "\n + Buffer overflows"
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
self.fuzz_loop(overflowstrings, first_part, last_part, 0, checklen, lvalue, middle_part, len_position, lformat)
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
#fuzzing format strings
if fuzzutils.context["xmllog"].fuzz_fs(fuzzutils.context["xmldoc"], requestid, fuzzid)=='1':
logtemp = "\n + Format Strings"
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
self.fuzz_loop(formatstrings, first_part, last_part, 0, checklen, lvalue, middle_part, len_position, lformat)
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
#fuzzing integer strings
if fuzzutils.context["xmllog"].fuzz_int(fuzzutils.context["xmldoc"], requestid, fuzzid)=='1':
logtemp = "\n + Integer Overflows"
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
#Numbers in Ascii
self.fuzz_loop(integerstrings, first_part, last_part, 1, checklen, lvalue, middle_part, len_position, lformat)
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
#Numbers in little Endian
self.fuzz_loop(integerstrings_little_endian, first_part, last_part, 0, checklen, lvalue, middle_part, len_position, lformat)
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
#Numbers in big Endian
self.fuzz_loop(integerstrings_big_endian, first_part, last_part, 0, checklen, lvalue, middle_part, len_position, lformat)
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
if fuzzutils.context["xmllog"].fuzz_dict(fuzzutils.context["xmldoc"], requestid, fuzzid)=='1' and fuzzutils.context["dictionary"]!='':
logtemp = "\n + Dictionary Attack"
dictionary_file = open(fuzzutils.context["dictionary"])
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),logtemp + '\n')
self.fuzz_loop(dictionary_file, first_part, last_part, 0, checklen, lvalue, middle_part, len_position, lformat)
dictionary_file.close()
if fuzzutils.context["stopfuzzing"]!=0: # The user decided to stop fuzzing
break
if fuzzutils.context["debug"]:
self.file_object.close()
if fuzzutils.context["stopfuzzing"]==1: # The user decided to stop fuzzing
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),"\n[*] Fuzzing session finished by user.\n")
fuzzutils.context["stopfuzzing"]=0
elif fuzzutils.context["stopfuzzing"]==2:
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),"\n[*] Fuzzing session because remote server is not responding.\n")
fuzzutils.context["stopfuzzing"]=0
else:
fuzzutils.context["fuzzinglog"].insert(fuzzutils.context["fuzzinglog"].get_end_iter(),"\n[*] Fuzzing session finished.\n")
|