CodeGateCTF - Angrybird
Angrybird ha sido el primer reto que he resuelto en CodeGate2017 CTF
2017. El reto no tenía ningún hint.
Cuando se abre el binario con IDA
, se puede observar el flow-graph de ejecución general de la función main:
Si observamos más detenidamente, podemos observar que la cadena de if else está compuesta por basic blocks los cuales realizan una operación lógica, y si esta es satisfactoria el binario procederá a ejecutar el siguiente simple block. De lo contratio, el binario terminará su ejecución llamando a la función _exit
.
Cada uno de los basic blocks se encarga de validar un byte determinado en un string. Este string es inicializado por el usuario al principio de main por la función fgets
También podemos apreciar que al final de la función main, este string se imprime una vez que pasa todos los checks de cada basic block.
Una vez analizado el binario estáticamente, he decidido debuggearlo y de alguna manera intentar deducir si había algún patrón en la manera que los checks en los basic blocks comprobaban cada byte del input. Sin embargo, no pude ejecutar el binario. Esto fue debido a que al principio de la función main
hay una serie de llamadas a funciones que cuyo propósito es simplemente evitar la ejecución del programa. Para que el lector se haga una idea de que clase de instrucciones había en estas funciones, la siguiente rutina servirá como ejemplo:
mov eax, 0
test eax, eax
jz _exit
Cuando estaba haciendo este reto durante la noche del CodeGate, me resistí a parchear el binario para hacer análisis dinámico sobre él. Normalmente siempre hago análisis dinamico en cualquier reto de ingeniería inversa. Pero en este caso el número de checks y de basic blocks por los que había que pasar y depurar era demasiado alto, y tardaría demasiado en llegar a una conclusión productiva.
Sin embargo, decidí intentar resolver el reto mediante ejecución simbólica
.
El framework que utilicé, y uno de los más populares es angr (el mismo nombre del reto insinuaba utilizarlo).
Para poder escribir un script basado en angr
satisfactoriamente deberemos seguir los mismos pasos que seguiríamos en cualquier otro algoritmo de búsqueda convencional.
Primero hay que identificar el estado inicial
desde el cual empezará nuestro análisis. La dirección que utilicé para el estado inicial fue 0x004007C2
. Esta dirección es un offset en main después de las funciones de antiejecución.
Una vez que tenemos el punto de partida, necesitamos saber el estado final
a donde deseamos llegar. Utilicé la dirección 0x0404fbc
para ello. Esta dirección es la dirección de la llamada a puts al final de main. La cual es llamada solo si se pasaran satisfactoriamente todos los checks de cada basic block.
Vale, tenemos el punto de partida y el punto final de nuestra búsqueda. Sin embargo, si queremos que nuestro script corra un poco más rápido, también podemos introducir una serie de estados al engine simbólico para que evite una serie de paths cuando esté buscando el camino a nuestro estado final.
El método que utilicé para encontrar estados a los que evitar fue encontrar todos los xrefs de _exit
. La razón de ello es porque la función _exit
es llamada siempre que uno de los checks de cada basic block no es satisfactorio. Por lo tanto podemos asumir que si hay una llamada a _exit
significa que el input es erróneo.
Teniendo todo esto en cuenta, y con un poco de magia con vim, escribí el siguiente script:
import sys
import angr
import simuvex
import claripy
p = angr.Project('angrybird')
e = p.factory.blank_state(addr=0x004007c2)
frame_addr = e.regs.rbp
frame_val = e.se.BVS('flag', 100*8)
e.memory.store(frame_addr, frame_val)
pg = p.factory.path_group(e)
to_avoid = [
0x4007fe,
0x400827,
0x400847,
0x400870,
0x40089f,
0x4008c8,
0x4008f1,
0x40091a,
0x400943,
0x400986,
0x4009af,
0x4009d8,
0x400a01,
0x400a2a,
0x400a56,
0x400a7b,
0x400aa4,
0x400aca,
0x400aef,
0x400b18,
0x400b4e,
0x400b77,
0x400bb3,
0x400be5,
0x400c0e,
0x400c40,
0x400c6c,
0x400c91,
0x400cba,
0x400cdf,
0x400d04,
0x400d2a,
0x400d4f,
0x400d78,
0x400da1,
0x400dc1,
0x400de6,
0x400e0f,
0x400e35,
0x400e61,
0x400e8a,
0x400ec7,
0x400eec,
0x400f15,
0x400f3e,
0x400f67,
0x400f87,
0x400fb0,
0x400fdf,
0x401004,
0x40102a,
0x40105a,
0x40107f,
0x4010a8,
0x4010d1,
0x4010f1,
0x40111a,
0x401143,
0x40116c,
0x401195,
0x4011d1,
0x4011fa,
0x40122a,
0x401253,
0x40127c,
0x4012a5,
0x4012ce,
0x4012f7,
0x40132d,
0x401356,
0x401382,
0x4013ab,
0x4013d4,
0x4013f4,
0x40141d,
0x401446,
0x40146f,
0x401498,
0x4014c1,
0x4014ea,
0x401513,
0x40153c,
0x401565,
0x40158e,
0x4015b7,
0x4015e0,
0x40160f,
0x401638,
0x401661,
0x40168a,
0x4016b3,
0x4016e2,
0x40170b,
0x401734,
0x40178a,
0x4017b3,
0x4017db,
0x401804,
0x40182d,
0x401863,
0x40188c,
0x4018c2,
0x4018eb,
0x401914,
0x40193d,
0x401966,
0x40198f,
0x4019b8,
0x4019e1,
0x401a0a,
0x401a33,
0x401a62,
0x401a8b,
0x401abb,
0x401ae4,
0x401b0d,
0x401b3c,
0x401b65,
0x401b8e,
0x401bb3,
0x401bdc,
0x401c04,
0x401c3a,
0x401c69,
0x401c92,
0x401cbb,
0x401ce4,
0x401d0d,
0x401d32,
0x401d58,
0x401d7d,
0x401da6,
0x401de2,
0x401e0b,
0x401e34,
0x401e59,
0x401e7f,
0x401ea4,
0x401ec9,
0x401ef2,
0x401f21,
0x401f4a,
0x401f73,
0x401f9c,
0x401fc5,
0x401fee,
0x402017,
0x402040,
0x402069,
0x402092,
0x4020b7,
0x4020dc,
0x402102,
0x402127,
0x402150,
0x402179,
0x4021af,
0x4021d8,
0x40220e,
0x402233,
0x40226c,
0x402292,
0x4022b7,
0x4022e0,
0x402309,
0x402332,
0x40235b,
0x402384,
0x4023ad,
0x4023d2,
0x4023f7,
0x40241c,
0x402442,
0x402467,
0x40249d,
0x4024cf,
0x40250b,
0x402534,
0x402559,
0x40257e,
0x4025a4,
0x4025c9,
0x4025f2,
0x40264f,
0x402681,
0x4026a6,
0x4026d5,
0x4026fe,
0x40272e,
0x402757,
0x40278d,
0x4027b6,
0x4027dc,
0x402801,
0x40282a,
0x402853,
0x40287c,
0x4028a5,
0x4028ce,
0x402900,
0x402929,
0x402952,
0x402977,
0x40299c,
0x4029c5,
0x4029ea,
0x402a24,
0x402a49,
0x402a78,
0x402aa1,
0x402aca,
0x402b00,
0x402b29,
0x402b52,
0x402b88,
0x402bad,
0x402bd6,
0x402bff,
0x402c24,
0x402c49,
0x402c78,
0x402ca1,
0x402cc6,
0x402cec,
0x402d11,
0x402d3a,
0x402d70,
0x402d99,
0x402dc2,
0x402de7,
0x402e10,
0x402e3e,
0x402e67,
0x402e99,
0x402ebe,
0x402eed,
0x402f12,
0x402f3b,
0x402f64,
0x402f9f,
0x402fc5,
0x402ff7,
0x403020,
0x403049,
0x403069,
0x403092,
0x4030bb,
0x4030e4,
0x40310d,
0x40312d,
0x403163,
0x40318c,
0x4031ac,
0x4031d5,
0x4031fe,
0x403227,
0x403250,
0x403279,
0x4032a2,
0x4032e9,
0x403316,
0x40333f,
0x403368,
0x4033a4,
0x4033da,
0x4033fa,
0x403423,
0x40344c,
0x403475,
0x40349e,
0x4034c7,
0x4034f0,
0x403510,
0x403539,
0x403568,
0x403591,
0x4035ba,
0x4035e3,
0x403619,
0x403639,
0x40366f,
0x403698,
0x4036c1,
0x403711,
0x40373a,
0x403763,
0x403799,
0x4037cf,
0x4037f8,
0x403821,
0x40384a,
0x403873,
0x40389c,
0x4038d2,
0x403908,
0x40393e,
0x403967,
0x40398c,
0x4039b2,
0x4039d7,
0x403a00,
0x403a29,
0x403a52,
0x403a7b,
0x403aa0,
0x403acf,
0x403af4,
0x403b23,
0x403b4c,
0x403b75,
0x403b9e,
0x403bc7,
0x403bf0,
0x403c15,
0x403c3a,
0x403c69,
0x403c92,
0x403cbb,
0x403cf1,
0x403d1a,
0x403d43,
0x403d6c,
0x403d91,
0x403db6,
0x403de5,
0x403e0e,
0x403e37,
0x403e60,
0x403e9f,
0x403ee0,
0x403f16,
0x403f3f,
0x403f75,
0x403f9a,
0x403fbf,
0x403fe5,
0x40400a,
0x404033,
0x404069,
0x404092,
0x4040bb,
0x4040e0,
0x404105,
0x40412b,
0x404150,
0x404179,
0x4041a2,
0x4041d8,
0x404201,
0x404227,
0x40424c,
0x404275,
0x40429e,
0x4042c7,
0x4042f0,
0x404319,
0x40433e,
0x404367,
0x40438c,
0x4043b1,
0x4043e3,
0x404409,
0x40442e,
0x404457,
0x404480,
0x4044a9,
0x4044d2,
0x4044fb,
0x404520,
0x404549,
0x40456e,
0x404593,
0x4045bc,
0x4045e1,
0x404607,
0x40462c,
0x404655,
0x40467e,
0x4046a7,
0x4046d0,
0x4046f9,
0x40471e,
0x404747,
0x40476c,
0x404791,
0x4047ba,
0x4047df,
0x404804,
0x404829,
0x40484f,
0x404874,
0x40489d,
0x4048c6,
0x4048eb,
0x404910,
0x40493f,
0x404968,
0x404991,
0x4049ba,
0x4049e3,
0x404a0c,
0x404a2c,
0x404a55,
0x404a7e,
0x404aa7,
0x404ad0,
0x404af9,
0x404b19,
0x404b42,
0x404b6b,
0x404b94,
0x404bbd,
0x404be6,
0x404c0f,
0x404c38,
0x404c7b,
0x404ca4,
0x404ccd,
0x404cf6,
0x404d1f,
0x404d48,
0x404d7e,
0x404da7,
0x404dd0,
0x404df9,
0x404e22,
0x404e4b,
0x404e70,
0x404e9f,
0x404ec8,
0x404ef1,
0x404f1a,
0x404f3f,
0x404f77,
0x404fa6]
pg.explore(find=0x0404FBC, avoid=to_avoid)
if len(pg.found) == 0:
print "not found anything"
sys.exit()
s = pg.found[0].state
print "flag", s.se.any_str(s.memory.load(frame_addr-0x50, 20))
Al ejecutar el script podemos ver nuestra flag que es: Im_so_cute&pretty_:)