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/).

Follow
Un comentario