AgriWeb
AgriWeb
Platform: HackTheBox | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-02-01 | Status: Solved Techniques: code_review, deepmerge_sanitization, prototype_pollution_fix
Summary
Task: Patch a prototype pollution vulnerability in a Node.js Express web app to get the flag. Solution: Identify unfiltered proto/constructor keys in a deepMerge function, upload a patched version that filters dangerous keys, restart the app, and verify the fix via /api/verify endpoint.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
hackthebox| ID:20260201_hackthebox_agriweb - Tags: nodejs, javascript, prototype_pollution, express, api, code_patching
- Indicators: deepMerge function, recursive object merge, proto not filtered, constructor not filtered, Express.js application
- Source:
20260201_hackthebox_agriweb.md
Foothold
Vulnerability / Misconfiguration
- Code_review
- Deepmerge_sanitization
- Prototype_pollution_fix
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- code_review
- deepmerge_sanitization
- prototype_pollution_fix
- Tags: nodejs, javascript, prototype_pollution, express, api, code_patching
Original Writeup
<details><summary>Click to expand original content</summary>AgriWeb - HackTheBox CTF
Description
"Digital farmlands lie ruined as drones spin out of control and greenhouses overheat; the white-hats must infiltrate the corrupted AgriWeb interface and bring the fields back to life."
Target: http://94.237.120.112:57056
Analysis
Initial Reconnaissance
Accessed the target URL and found an "HTB Editor" - a code editor interface with API access to the application source code.
API Endpoints Discovered
/api/directory- List files/api/file?path=X- Read file contents/api/create-file- Create new files/api/delete- Delete files/api/restart- Restart application/api/verify- Verify if vulnerability is patched
Application Structure
app.js - Main Express application
routes/auth.js - Authentication routes
routes/profile.js - Profile update routes (VULNERABLE)
utils/jwt.js - JWT token handling
utils/database.js - SQLite database setup
exploit/solver.py - Hint file showing the attack
Vulnerability: Prototype Pollution in deepMerge
Found in routes/profile.js:
function deepMerge(target, source) {
for (let key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
Problem: This function doesn't filter dangerous keys (__proto__, constructor, prototype), allowing prototype pollution.
Impact: The JWT token generation in utils/jwt.js checks user.role === 'admin' to set isAdmin: true. By polluting the Object prototype with isAdmin: true, any user could bypass admin authentication.
The Twist - Code Patching Challenge
The /api/verify endpoint returned: "Application vulnerability is not patched."
This revealed the challenge wasn't about exploiting the vulnerability, but patching it!
Solution
Step 1: Delete the vulnerable file
curl -X DELETE "http://94.237.120.112:57056/api/delete" \
-H "Content-Type: application/json" \
-d '{"path":"routes/profile.js"}'
Step 2: Create patched version with prototype pollution protection
The fix adds a check to skip dangerous prototype pollution keys:
function deepMerge(target, source) {
for (let key in source) {
// PATCH: Filter dangerous prototype pollution keys
if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
Step 3: Upload the patched file
curl -X POST "http://94.237.120.112:57056/api/create-file" \
-H "Content-Type: application/json" \
-d '{"path":"routes/profile.js","content":"<patched code>"}'
Step 4: Restart the application
curl -X POST "http://94.237.120.112:57056/api/restart"
Step 5: Verify and get flag
curl "http://94.237.120.112:57056/api/verify"
# Response: {"flag": "HTB{REDACTED}"}
Secure Coding Fix
Always filter these keys in recursive merge functions:
__proto__- Direct prototype accessconstructor- Access to constructor.prototypeprototype- Direct prototype property
// Safe deepMerge implementation
function deepMerge(target, source) {
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
for (let key in source) {
if (DANGEROUS_KEYS.includes(key)) continue;
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
Lessons Learned
- Prototype pollution in JavaScript occurs when user input can modify Object.prototype
- Always sanitize keys in recursive merge/deep copy functions
- The dangerous keys to filter are:
__proto__,constructor,prototype - Some CTF challenges require fixing vulnerabilities rather than exploiting them
- Code editor interfaces can expose full application source for analysis
Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR