Unlock Me [905]
Category: Web
Opening the webpage we try to login with the credentials given to us
user: minion, password: banana
. This however returns a message saying that only admins are allowed into HQ.
Using a proxy like ZAP allows us to inspect the request further. We notice that the login process consists of 2 steps.
-
A post request is sent to the server with the credentials. This returns an
accessToken
that resembles a JSON Web Token (JWT). -
A GET request is then made with the access token in the
Authorization
request header.
Extracting the access token, we can view its contents either by manually decoding the base64, using an online tool such as jwt.io or using any tool of your choice.
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "RS256"
[+] typ = "JWT"
Token payload values:
[+] username = "minion"
[+] role = "user"
[+] iat = 1607533875 ==> TIMESTAMP = 2020-12-10 01:11:15 (UTC)
Baed on the error message, it is quite clear that we have to somehow change the role from “user” to “admin” and at the same time, update the timestamp. At this point, the first way I thought to bypass the signing was to set the algorithm to None
. This essentially means that no signing is required. However, this vulnerability will only affect unpatched implementations of JWT. In this case, "alg":"None"
did not work. Next I thought of trying to crack the JWT password, but that only works if the JWT used the HS256
algorithm which is uses symmetric encryption to sign the token. At this point, I was stuck and decided to go back to the site for clues that I may have missed. Sure enough, I did miss out something. Found in the HTML was a TODO comment
// TODO: Add client-side verification using public.pem
This immediately reminds us of another JWT vulnerability. We can change the JWT algorithm to HS256. So what happens when we change the signing algorithm from symmetric to asymmetric. Well, if the algorithm is not specified when verifying the token, the server might simply use the public key to verify the token. So what are the implications of this?
For tokens using HS256
, the key is meant to be kept secret, whereas with RS256, the public key is known. If the key used for HS256
is compromised, then we can craft any token we want.
To download, the public key, we can simply append /public.pem
to the back of the url. Now we just need to change the contents which is simple base64 encoding and sign the token with public.pem
.
Since I’m lazy to code and there are already many existing tools on github for JWTs I just used this tool to sign the token. Our tampered jwt looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1pbmlvbiIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTYwNzUzNDg3NX0.Unoy8MAMqqoEqqLVWf5DQ6_oljR1L9f8oahKA9Zp8SQ
Its decoded contents:
=====================
Decoded Token Values:
=====================
Token header values:
[+] alg = "HS256"
[+] typ = "JWT"
Token payload values:
[+] username = "minion"
[+] role = "admin"
[+] iat = 1607534875 ==> TIMESTAMP = 2020-12-10 01:27:55 (UTC)
Now we just need to submit use this token to make the GET request and sure enough we have our flag.
Flag: govtech-csg{5!gN_0F_+h3_T!m3S}
This took me a while because I only saw the hint after about 30 mins to an hour of trying. I guess my biggest takeaway from this challenge is to always do recon properly first and don’t just blindly jump straight in and try exploit.