Hace poco escribí acerca de como hacer un script para fuzzear aplicaciones locales con Sulley. Aprovechando esta base, he creado, junto con mi amigo Roi, otro script orientado a fuzzear activex. Está sin probar bien por lo que posiblemente requiera de algunos retoques, pero puede servir como base.
Las diferentes opciones son:
- -l -> lista todos los activex instalados en Windows.
- -n -> tiene en cuenta los que hay en exclude.txt y muestra los que no están ahí. Esta opción es bueno para tener ahí metidos todos los que aparezcan en tu máquina virtual y luego, localizar rápidamente los que instales.
- -s NAME -> busca por aproximación el nombre de un componente.
- -d ID -> igual que antes pero con el CSLID, es decir, busca por aproximación el ID del activex.
- -m FILE -> aquí pasamos la ruta del activex y nos mostrará todos los métodos y parámetros que acepta.
- -f FILE -> de forma similar al comando anterior, abre el activex e intenta fuzzear cada parámetro, con ayuda del potencial de Sulley.
El funcionamiento para fuzzear sería:
- Abre el activex.
- Enumera cada clase.
- Dentro de cada clase coge todos los métodos.
- Por cada método mira el número de parámetros que tiene y realiza todas las combinaciones de mutación.
- Luego continúa con el siguiente parámetro.
- Luego el siguiente método.
- Y el mismo proceso con la siguiente clase.
Por cada prueba se genera un fichero diferente. Y cuando lleva 100 ficheros generados, lanza el Internet Explorer para comenzar las pruebas.
Los ficheros generados son del tipo:
// file: c:/sulley/test0.html <META HTTP-EQUIV='Refresh' CONTENT='3; URL=file:///c:/sulley/test1.html'> <html> <body> <object classid='clsid:GUID id='target' /> <script language='vbscript'> target "XXXXXXX" </script> </body> </html>
De manera que tras lanzar el primer test, se va recargando el sólo con el siguiente y así evitamos tener que abrir y cerrar el Internet Explorer en cada prueba. Por defecto he puesto 3 segundos pero se puede cambiar en el script.
El fichero de sesiones sería el siguiente:
# script to fuzz ActiveX # # by: # Pepelux <pepeluxx@gmail.com> - http://www.pepelux.org/ && http://www.enye-sec.org/ import win32api,win32con,pythoncom,sys,os,win32com.client from win32com.axscript import axscript import optparse from sulley import * from requests import activex_generic # library to fuzz from pydbg import * from pydbg.defines import * import os import threading import sys from time import sleep tmpfolder=r"c:\sulley\tmp" iexplorepath=r"c:\Archivos de programa\Internet Explorer\iexplore.exe" refreshTime=3 ################################ ### ACCESS VIOLATION HANDLE #### ################################ def av_handler(dbg): print "[x] Access Violation Handler" print dbg.dump_context() try: mod = dbg.addr_to_dll(dbg.context.Eip) print 'Eip module path : ', mod.path print 'Eip module base : 0x%08x'%mod.base print 'Eip offset : 0x%08x '%(dbg.context.Eip - mod.base) except: print 'Unable resolve Eip module' global stop stop=1 dbg.stack_unwind() for i in dbg.disasm_around(dbg.context.Eip, 12): print "%x %s"%i dbg.terminate_process() return DBG_CONTINUE ################################## ### START PROCESS USING PYDBG #### ################################## class start_proc (threading.Thread): def __init__(self, t_proc_name, t_name): threading.Thread.__init__(self) self.t_proc_name = t_proc_name self.t_name = t_name def run(self): dbg = pydbg() dbg.load(self.t_proc_name, self.t_name) dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, av_handler) dbg.run() ############################################# ### GET COMPONENTS FROM WINDOWS REGISTRY #### ############################################# def getCLSID( type, name ): index=0 count=0 clsid_list=[] #getting registry keys try: cKey=win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,"SOFTWARE\\Classes") except win32api.error: print"[x] Error opening registry keys." sys.exit(0) #getting subkeys while True: try: sKey=win32api.RegEnumKey(cKey,index) except win32api.error: break progid=sKey #getting CLSID registry keys try: sKey=win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,"SOFTWARE\\Classes\\%s\\CLSID" % progid) except win32api.error: index+=1 continue #getting CLSID values try: clsid=win32api.RegQueryValueEx(sKey,None)[0] except win32api.error: print"[x] Error getting CLSID value." index+=1 continue clsid_list.append((progid,clsid)) index+=1 if name == "new": wlist=[] try: file = open("exclude.txt") while 1: line=file.readline() wlist.append(line) if not line: break pass file.close() except: print "[x] File 'exclude.txt' not found" sys.exit(0) lastComName="" lastComNameIni="" lastComId="" for clsid in clsid_list: comName=clsid[0] comNameIni=clsid[0][0:clsid[0].find(".")] comId=clsid[1] show=1 if name == "new": for value in wlist: if comNameIni.replace("\n","") == value.replace("\n",""): show=0 if show == 1: if comName != lastComName and comId != lastComId: if lastComNameIni != comNameIni: print "\n" try: if type == "name": if name == "all" or name == "new" or comName.lower() == name.lower() or comName.lower().index(name.lower())>-1: print"[+] "+comName + " - " + comId else: if name == "all" or name == "new" or comId.lower() == name.lower() or comId.lower().index(name.lower())>-1: print"[+] "+comName + " - " + comId count+=1 except: continue lastComName=comName lastComNameIni=comNameIni lastComId=comId if count == 0: print "[x] No entries found\n" sys.exit(0) #################################### ### GET METHODS FROM A DLL FILE #### #################################### def getTypes( activex ): VTS={} try: tlb=pythoncom.LoadTypeLib(activex) except: print"[x] Error loading library." sys.exit(0) name=tlb.GetDocumentation(-1)[0] print "[+] Name: "+name clsid=str(tlb.GetLibAttr()[0]) print "[+] CLSID: "+clsid+"\n" for vt in [x for x in pythoncom.__dict__.keys() if x.count("VT_")]: VTS[eval("pythoncom.%s" % vt)]=vt for i in xrange(tlb.GetTypeInfoCount()): name=tlb.GetDocumentation(i)[0] print " [-] Interface: "+name info=tlb.GetTypeInfo(i) attr=info.GetTypeAttr() print " [-] GUID: "+str(attr.iid) print" [%d] Properties:" % i try: for j in xrange(attr.cVars): id=info.GetVArDesc(j)[0] names=info.GetNames(id) print"[%d/%d]\t%s" % (i,j,names[0]) except: continue lastMethodName="" print"\t+ Methods:" for k in xrange(attr.cFuncs): desc=info.GetFuncDesc(k) if desc.wFuncFlags: continue id=desc.memid names=info.GetNames(id) methodName = names[0] if methodName != lastMethodName: print"\t\t%s" % (methodName) k=0 lastMethodName=methodName try: for name in names[1:]: print"\t\t\t%s,%s" % (name, VTS[desc.args[k][0]]) k+=1 except: continue print"\n" sys.exit(0) ########################### ### FUZZING A DLL FILE #### ########################### def startFuzz( activex ): VTS={} try: tlb=pythoncom.LoadTypeLib(activex) except: print"[x] Error loading library." sys.exit(0) if not os.path.isdir(tmpfolder): os.mkdir(tmpfolder) for file in os.listdir(tmpfolder): file_path = os.path.join(tmpfolder, file) try: if os.path.isfile(file_path): os.unlink(file_path) except: break name=tlb.GetDocumentation(-1)[0] clsid=str(tlb.GetLibAttr()[0]) counter=0 for vt in [x for x in pythoncom.__dict__.keys() if x.count("VT_")]: VTS[eval("pythoncom.%s" % vt)]=vt print "[+] Total classes for fuzzing: " + str(tlb.GetTypeInfoCount()) + "\n" for i in xrange(tlb.GetTypeInfoCount()): name=tlb.GetDocumentation(i)[0] info=tlb.GetTypeInfo(i) attr=info.GetTypeAttr() guid=str(attr.iid).replace("{","").replace("}","") ac_class=name lastMethodName="" for k in xrange(attr.cFuncs): desc=info.GetFuncDesc(k) if desc.wFuncFlags: continue id=desc.memid names=info.GetNames(id) methodName = names[0] if methodName != lastMethodName: print "\n-> " + ac_class + "->" + methodName + " {" + guid + "}" k=0 lastMethodName=methodName try: for name in names[1:]: k+=1 except: continue # ----- Start Fuzz ----- for j in range (0, k): type="string" try: if VTS[desc.args[j][0]] == "VT_BSTR" or VTS[desc.args[j][0]] == "VT_VARIANT" or VTS[desc.args[j][0]] == "VT_ARRAY": print " [-] Creating fuzzing cases for arg " + str(j+1) + " of " + str(k) + " (hex)" type="hex" else: if VTS[desc.args[j][0]] == "VT_BSTR" or VTS[desc.args[j][0]] == "VT_BOOL" or VTS[desc.args[j][0]] == "VT_VARIANT" or VTS[desc.args[j][0]] == "VT_UII" \ or VTS[desc.args[j][0]] == "VT_I2" or VTS[desc.args[j][0]] == "VT_I4" or VTS[desc.args[j][0]] == "VT_EMPTY" or VTS[desc.args[j][0]] == "VT_NULL" \ or VTS[desc.args[j][0]] == "VT_CY" or VTS[desc.args[j][0]] == "VT_DECIMAL" or VTS[desc.args[j][0]] == "VT_R4" or VTS[desc.args[j][0]] == "VT_R8": print " [-] Creating fuzzing cases for arg " + str(j+1) + " of " + str(k) + " (integer)" type="integer" else: print " [-] Creating fuzzing cases for arg " + str(j+1) + " of " + str(k) + " (string)" type="string" req=s_get("string") # session except: print " [-] Creating fuzzing cases for arg " + str(j+1) + " of " + str(k) + " (string)" type="string" req=s_get(type) # session req.reset() for i in range(req.names["ini"].num_mutations()): data=[] data.append("<META HTTP-EQUIV='Refresh' CONTENT='"+str(refreshTime)+"; URL=file:///"+tmpfolder.replace("\\", "/")+"/test"+str(counter+1)+".html'>\n") data.append("<html>\n<body>\n") data.append("<object classid='clsid:"+guid+"' id='target' />\n") data.append("<script language='vbscript'>\n") targetStr = "target." + methodName + " " for l in range (0, k): if j == l: if type == "string": targetStr += "\"" try: targetStr += s_render() except: targetStr += s_render().encode("hex") if type == "string": targetStr += "\"" else: if type == "string": targetStr += "\"XX\"" else: targetStr += "00" if l<k-1: targetStr += ", " targetStr += "\n" data.append(targetStr) data.append("</script></body>\n</html>\n") file = open(tmpfolder+"\\test"+str(counter)+".html","w") file.writelines(data) file.close() counter+=1 s_mutate() if counter == 100: print "Starting fuzzing" t = start_proc(iexplorepath, tmpfolder + "\\test0.html") t.start() # ----- End Fuzz ----- print "\n[+] Creation files has ended\n" print "\n" sys.exit(0) ############# ### INIT #### ############# def main(): os.system("cls") parser = optparse.OptionParser() parser.add_option('-l', dest='lst', action='store_true', help='list all components') parser.add_option('-n', dest='new', action='store_true', help='hidden windows components') parser.add_option('-s', dest='name', help='search components by name (ex: -s Office)') parser.add_option('-i', dest='id', help='search components by clsid (ex: -s {xxx-xxx-xxx-xxx})') parser.add_option('-m', dest='file', help='list methods by file (ex: -m c:\\path\\activex.dll)') parser.add_option('-f', dest='fuzz', help='fuzz file (ex: -f c:\\path\\activex.dll)') (opts, args) = parser.parse_args() if not opts.lst and not opts.new and not opts.name and not opts.id and not opts.file and not opts.fuzz: print "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" print "COM Activex fuzzer (by Pepelux && Roi)\n" print "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" parser.print_help() else: print "[+] Starting analysis ...\n" if opts.lst is not None: getCLSID("", "all") if opts.new is not None: getCLSID("", "new") if opts.name is not None: getCLSID("name", opts.name) if opts.id is not None: getCLSID("id", opts.id) if opts.file is not None: getTypes(opts.file) if opts.fuzz is not None: startFuzz(opts.fuzz) if __name__=="__main__": main()
Y el correspondiente request:
# script to fuzz ActiveX # # by: # Pepelux <pepeluxx@gmail.com> - http://www.pepelux.org/ && http://www.enye-sec.org/ from sulley import * ################################################## s_initialize("string") if s_block_start("ini"): s_string("A") s_block_end() s_initialize("integer") if s_block_start("ini"): s_int(0, format="ascii", signed=True) s_block_end() s_initialize("hex") if s_block_start("ini"): s_random("\xff", min_length=1, max_length=500) s_block_end()
Un saludo a todos, en especial a Roi (http://elotrolad0.blogspot.com/).
Un comentario