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:

main's graph

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.

basic block

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

input

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.

final

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_:)