Post

SSTI 1 – picoCTF Walkthrough

SSTI 1 – picoCTF Walkthrough

Challenge: Server-Side Template Injection (SSTI)

Description:
I made a cool website where you can announce whatever you want! Try it out!
I heard templating is a cool and modular way to build web apps!

Hint:
Server Side Template Injection


What is SSTI?

Sever-Side Template Injection (SSTI) is a vulnerability where user input is directly rendered in a server-side template, allowing attackers to inject and execute code on the server.

If the template engine doesn’t sanitize user input, things like this become dangerous: Hello

If name is user-controlled, the attacker could inject: `` Which renders:Hello 49

Now imagine instead of math, the attacker injects Python code to run commans or read files.

What the Site Looked Like

The site was simple with a text box beside the question “What do you want to announce?”

Whatever you typed was reflected on the page. Classic reflection = test for SSTI

Step 1: Testing the Template Engine

I started small by typing: ``

The site returned 49

This meant that it was vulnerable to SSTI, and likely using the jinja2 template engine (used by Flask and many Python apps)

Step 2: Escalating with Object Traversal

To gain deeper access, I use this payload: ``

What does this do?

  • config: A Flask object in Jinja2’s environment.
  • .__class__.__init__.__global__: Gives access to Python’s global variable from the class constructor
  • .popen('ls').read(): Run a shell command and returns the output

In short: this breaks out of the sandbox and let you run system commands.

Step 3: Viewing the Files

The output of the above command showed:

1
2
3
app.py
templates/
flag requirements.txt

flag requirement.txt is almost always what we need in a CTF

Step 4: Reading the Flag

To the read the flag, the following was ran ``

The following flag was printed: picoCTF{s4rv3r_s1d3_t3mp14t3_1nj3ct10n5_4r3_c001_ae48ad61}

What I learned

  • SSTI can be powerful and dangerous - from math to full remote code execution.
  • Knowing the internals of Jinja2 (or any engine) is helpful in crafting payloads.
  • Object traversal like config.__class__.__init__.__globals__ is a key way to break out of templates.

Other Templates Engines to Watch Out For

| Engine | Language | Syntax Example | Known for SSTI? | |————-|———-|—————-|——————| | Jinja2 | Python | | Yes | | Twig | PHP | | Yes | | Smarty | PHP | {$...} | Yes | | Velocity | Java | $variable | Yes | | Freemarker | Java | ${...} | Yes | | EJS | Node.js | <%= ... %> | Yes | | Razor | .NET | @{...} | Yes (rare) |

Always test different expressions like , ${77}, <%= 77 %> to see what sticks.

How to Prevent SSTI

Developers: Here’s how to stay safe:

  • Don’t use render_template_string() with raw user input.
  • Always sanitize or escape user input in templates.
  • Use whitelisted template variables.
  • Avoid evaluating user-controlled strings in your rendering logic.
  • Use template engine settings to restrict dangerous features.

Payload Cheat Sheet

| Purpose | Payload | |—————-|————————————————————————-| | Confirm SSTI | | | Access config | | | List files | | | Read flag.txt | |

Summary

This was a classic SSTI challenge — starting with a simple reflected input, moving to evaluating math, then escalating to reading the server’s filesystem. Understanding Python’s object model and Jinja2 internals gave me full control.

This post is licensed under CC BY 4.0 by the author.