Note
web
python
ssti
jinja2
template-injection
email-validation
rce
Smuggle a Jinja2 expression inside an email display name that survives weak validation and is later rendered server-side.
Jinja2 SSTI Hidden in an RFC 5322 Email Field
The application thinks it is validating an email address, but the template engine later renders the entire logical field — including the RFC 5322 display name. Many validators only enforce "looks like an email somewhere inside" and do not model mailbox syntax precisely, so attacker-controlled display names survive to template rendering.
Why It Works
- Validators commonly blacklist characters in the
local@domainpart while ignoring quoted display names around it. - The full field is later passed to Jinja2 in an email preview, admin panel, or confirmation page.
Vulnerable Pattern
- Signup, contact, or invite flows that store a display string and then render it into an email preview / admin view using Jinja2.
- Email "validation" that accepts
"<expr>" <a@b.com>because a valid mailbox exists somewhere inside.
Exploit Flow
- Start with a harmless display-name expression to see whether Jinja evaluation happens at all.
- If direct globals are filtered, pivot through commonly exposed objects:
lipsum,cycler,joiner,namespace. - Once object access works, reach
__globals__,__builtins__,__import__, then pivot toos,subprocess, or file reads.
"{{7*7}}" <a@b.com>
Variations
- Use attribute-access tricks, bracket notation, or string concatenation if underscores/dots are filtered.
- Sometimes data exfiltration is enough — reading
config,request.headers, or env vars.
Common Blockers
- Jinja sandboxing, undefined objects, autoescaping confusion, or the field being re-serialized before rendering.
PoC Sketch
"{{ lipsum.__globals__['os'].popen('id').read() }}" <a@b.com>
Good Situations To Use It
- An email field is echoed back into an admin/preview page.
- Validation accepts a quoted display name in front of a real mailbox.
{{7*7}}renders as49somewhere downstream.
Sources
0xFUN2026/web/jinja