pwneglyph logo
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@domain part 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

  1. Start with a harmless display-name expression to see whether Jinja evaluation happens at all.
  2. If direct globals are filtered, pivot through commonly exposed objects: lipsum, cycler, joiner, namespace.
  3. Once object access works, reach __globals__, __builtins__, __import__, then pivot to os, 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 as 49 somewhere downstream.

Sources

  • 0xFUN2026/web/jinja