SSTI labs

ssti.vulnlab.dev

Five labs covering the server-side template injection surface most tools care about: textbook Jinja2 RCE via render_template_string, Python str.format() attribute-walk SSTI (a separate sink class), substring-blocklist bypass, sandbox bypass through an over-privileged registered helper, and second-order SSTI where storage and render happen on different requests.

Each lab's per-lab flag string is in app.config under VULNLAB_SSTI_<slug>. In Flask, Jinja2 templates have access to config — that's the textbook extraction path.

Labs

Jinja2 SSTI (render_template_string) flask.render_template_string
User input rendered as a Flask template. Full Jinja2 evaluation.
str.format() SSTI (Python format string) str.format(user=...)
User-controlled format template + str.format(user=obj). Attribute walks reach anywhere.
Jinja2 SSTI behind a substring blocklist flask.render_template_string (after substring blocklist)
render_template_string with a substring deny-list. Trivially bypassable.
Jinja2 SSTI inside a SandboxedEnvironment SandboxedEnvironment + over-privileged registered global
Sandbox blocks the textbook RCE chain. A registered global ruins it.
Second-order SSTI (stored draft, rendered later) stored snippet -> render_template_string in a later request
Save endpoint stores raw template text; preview endpoint renders it.

For tool builders

Source for every lab is published. Each lab page links to its own source via /source/<slug>. Each lab also exposes a JSON detection hint at /meta/<slug> (and an index at /meta/) describing what a scanner should report: CWE, subtype, sinks, exploit examples, success markers, tags.