June 16, 2026
How I Found a Working SSTI in an Online Store -and Why It Was Medium, Not Critical
A real-world case: bypassing client-side validation with Burp Suite, confirming Server-Side Template Injection in Jinja2, and hitting the…
Nexus0xFault
4 min read
A real-world case: bypassing client-side validation with Burp Suite, confirming Server-Side Template Injection in Jinja2, and hitting the sandbox wall.
Article body
⚠️ Disclaimer ⚠️_. This vulnerability was found and reported to the developers under responsible disclosure. A fix shipped a week later — I re-tested and confirmed it's resolved. The developers allowed me to publish this publicly on the condition that the service name stays hidden. So throughout this post the store is referred to as "Site A." This material is for educational purposes._
Introduction
Hi everyone! In this write-up I'll walk through how I discovered a working SSTI (Server-Side Template Injection) in an online store: how I bypassed client-side validation with Burp Suite, confirmed template injection, and why the bug ultimately landed at a medium severity — without reaching RCE.
Let's get started.
The target
The target is an online store I'll call Site A. A typical e-commerce app: a product storefront, registration, and a user account area.
Reconnaissance
Before any testing, I study the application: its business logic, available features, and user-input points. This builds a map of the attack surface. After browsing the storefront, I moved on to registration.
After signing up I was redirected to my account, greeted by a "Welcome, {name}" message and an orders section. A personalized greeting that interpolates the username is a classic injection candidate.
Hypothesis: XSS or SSTI?
The username is reflected on the page, so my first move was to test it for XSS and SSTI.
Step 1. Testing for XSS. When I typed a payload directly into the form, a message appeared saying such characters can't be submitted — validation kicked in.
But client-side validation is a convenience for the user, not a security boundary. Let's check the server.
Step 2. Bypassing it with Burp Suite. I set the name to a normal value, intercepted the request, and in Repeater swapped username for <>.
Pleasant surprise: the server does not filter the input — the request with special characters was accepted.
Step 3. Why XSS didn't work. However, when the account page loads, the injected characters are escaped (< → <). That means the output is protected against XSS — you can't inject an active script.
Pivoting to SSTI
The output is escaped, but the input isn't filtered — so it makes sense to test for server-side template injection.
I send the classic probe:
{{7*7}}{{7*7}}In the response, the name position shows 49. The expression was evaluated on the server. That's a direct confirmation of SSTI.
Identifying the engine. I send a string payload:
{{3*'3'}}{{3*'3'}}The result is 333. This behavior (string multiplication) is characteristic of Jinja2 (Python).
Attempting to escalate to RCE
Next I tried to push the SSTI toward command execution with a classic gadget chain:
{{''.__class__.__mro__[1].__subclasses__()}}{{''.__class__.__mro__[1].__subclasses__()}}But the response triggered the sandbox — the engine blocked access to dangerous attributes.
After experimenting with various chain variations, I hit the sandbox every time. RCE wasn't reachable — so I filed the report as an SSTI without RCE.
Summary and severity
- Type: Server-Side Template Injection (Jinja2)
- Entry point: the username field in the account area
- Root cause: no server-side validation; client-side validation bypassable via Burp Suite
- Confirmation: 77 → 49, 3'3' → 333
- Limitations: templating engine sandboxed → RCE unavailable; output escaped → XSS unavailable
- Severity: Medium
Impact
Even without RCE, a sandboxed SSTI is not "cosmetic":
- the ability to probe internal application objects and template logic;
- risk of sensitive data exposure if useful objects are reachable in context;
- potential denial of service (DoS) via "heavy" expressions;
- risk of a full sandbox escape with future code changes or an outdated Jinja2 version.
Remediation
- Never pass user input into the templating engine as part of the template itself — use parameters and the render context.
- Mirror validation on the server: client-side checks are not a security boundary.
- Apply an allow-list of permitted characters for the username.
- Keep context-aware output escaping (already implemented for XSS here — which is correct).
- If dynamic templates are truly needed — render only in a sandbox and keep Jinja2 up to date.
Disclosure timeline
- Day 0 — discovery and report sent to Site A support with reproduction steps.
- ≈ Day 7 — developers shipped a fix.
- After the fix — re-test: vulnerability resolved.
- Publication — agreed with the developers, with the service name redacted.
Conclusion
That's it. This case nicely illustrates an important point: client-side validation ≠ security, while output escaping and a templating sandbox significantly reduce — but don't eliminate — the risk. Thanks to everyone who read to the end!