Archive for the ‘Fuzzing’ Category

Fuzzeando con Sulley algunos activex

Viernes, diciembre 24th, 2010

En el anterior post puse un código en python para fuzzear activex con Sulley. Bien, pues como comenté, el código estaba casi sin probar y necesitaba algunos retoques. He cambiado algunas cosas y he probado un par de exploits que aparece en exploit-db y los resultados han sido muy buenos.

Vamos a ver algunas capturas:

Ecava IntegraXor Remote ActiveX Buffer Overflow PoC (http://www.exploit-db.com/exploits/15767/)

WMITools ActiveX Remote Command Execution Exploit 0day (http://www.exploit-db.com/exploits/15809/)

¡¡¡ Felices Fiestas a todos !!!

Fuzzeando activex con Sulley

Miércoles, diciembre 22nd, 2010

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:

  1. Abre el activex.
  2. Enumera cada clase.
  3. Dentro de cada clase coge todos los métodos.
  4. Por cada método mira el número de parámetros que tiene y realiza todas las combinaciones de mutación.
  5. Luego continúa con el siguiente parámetro.
  6. Luego el siguiente método.
  7. 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/).

Usando Sulley para fuzzear aplicaciones locales

Jueves, diciembre 2nd, 2010

Como comenté en el anterior post, Sulley es una herramienta impresionante para fuzzear aplicaciones, pero viene más preparada para atacar servidores y no para aplicaciones locales. Como la generación de mutaciones funciona muy bien, he creado un script que usa esta funcionalidad, para atacar aplicaciones locales que cargan ficheros por línea de parámetros, es decir, ficheros de tipo DOC, XLS, PDF, TXT, SWF, etc. Esto unido a pydbg de PaiMei para controlar las excepciones.

La verdad es que el script no tiene ningún misterio. Lo que hace básicamente es:

  1. Genera una mutación con Sulley
  2. Crea un fichero con el resultado
  3. Abre el programa a fuzzear pasando como parámetro el fichero generado
  4. Pausa de 5 segundos para dejar que abra correctamente y, para que el manejador de excepciones haga su trabajo en caso de haber un crasheo
  5. Si todo ha funcionado bien, cierra y lanza la siguiente mutación
  6. En caso de crasheo, muestra los datos por pantalla y termina

El fichero de sesiones variará poco de una aplicación a otra, teniendo que indicar únicamente la ruta del EXE y la extensión que se desea usar para el fichero. La librería se la tendrá que crear cada uno, en función a lo que se desee fuzzear (PDF, SFW, etc).

He creado un ejemplo sencillo para fuzzear BACnet OPC Client 1.0.24. Podeis ver la vulnerabilidad y descargar el programa aquí: http://www.exploit-db.com/exploits/15026/. Hace tiempo escribí un tutotrial sobre como explotar esta aplicación (para iniciados en el fantástico mundo de los Buffer Overflows en Windows) en el que lo hacíamos de forma manual todo. Lo podeis leer aquí: http://pepelux.org/download.php?f=papers/BACnet.pdf

Repito que con ese programa no tiene mucho sentido usar un fuzzer ya que se explota con una cadena demasiado larga. Pero sí que tendría sentido para usarlo en un navegador o en un lector de PDFs, ya que es donde Sulley nos permite usar su potencial y, con unas buenas librerías, se pueden hacer maravillas.

Aquí va el código de la sesión:

# script to fuzz SCADA BACnet
#
# by:
# Pepelux <pepeluxx@gmail.com> - http://www.pepelux.org/ && http://www.enye-sec.org/

from sulley import *
from requests import bacnet_generic    # library to fuzz
from time import sleep
from pydbg import *
from pydbg.defines import *
import os
import threading
import sys

req=s_get("bn")        # session
stop=0

tmpfolder="c:\\sulley\\tmp\\"
name=tmpfolder+"test.csv"
proc_path="C:\\Program Files\\SCADA Engine\\BACnet OPC Client\\";
proc_name="BACnOPCClient.exe";

################################
### ACCESS VIOLATION HANDLE ####
################################

def av_handler(dbg):
 print "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()

#####################
### STOP PROCESS ####
#####################

class stop_proc (threading.Thread):
 def __init__(self, t_proc_name):
 threading.Thread.__init__(self)
 self.t_proc_name = t_proc_name

 def run(self):
 os.system("taskkill /T /F /IM "+self.t_proc_name+">"+tmpfolder+"/null")

#############
### INIT ####
#############

if not os.path.isdir(tmpfolder):
 os.mkdir(tmpfolder)

for i in range(req.names["ini"].num_mutations()):
 file = open(name,"w")
 file.writelines(s_render())
 file.close()

 print "starting target process (session "+str(i)+")"
 t = start_proc(proc_path+proc_name, name)
 t.start()

 sleep(5)
 print "stopping target process"

 t = stop_proc(proc_name)
 t.start()
 t.join()

 if (stop == 0):
 s_mutate()
 else:
 sleep(2)
 sys.exit()

Y la mini-librería usada en este caso:

# script to fuzz SCADA BACnet
#
# by:
# Pepelux <pepeluxx@gmail.com> - http://www.pepelux.org/ && http://www.enye-sec.org/

from sulley import *

########################################################################################################################

s_initialize("bn")
if s_block_start("ini"):
 s_static("OPC_TAG_NAME,OBJECT_TYPE,INSTANCE,OBJECT_NAME\n\\")
 s_string("A", size=400, padding="A")
 s_static("\\<OPC Server Name>\\<OPC Tag Name>,0,0,\n")
s_block_end()

Si a alguien se le ocurre alguna forma de mejorar esto que me mande un mail :)

Un saludo a Roi y a Boken, que me han ayudado con muchas dudas

Sulley Fuzzing Framework

Sábado, noviembre 27th, 2010