DEV Community

BaconPotatoCat
BaconPotatoCat

Posted on • Updated on

[Stacks 2020 CTF] Unlock Me - Web

Challenge Text


challenge text

Unlock Me - Challenge Text

Introduction

This is the write-up for the Unlock Me Web Challenge from the team Ov3rWr1t3.

Unlock Me was a 1000 point challenge. Credentials were provided right off the bat, along with a login page with the URL of http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41031/.

Solution

Initial login attempt

Right away, we attempted to login to the panel with the given credentials to see what would happen. We were greeted with the following message.


failed_login

Failed login message

Ah, it seems that there is Role Based Access Control of some sorts being implemented on the login API. Perhaps we could get more information by intercepting the request on Burpsuite?


burp_intercept_1

Successful Login Response

How interesting. An authorization header with a Bearer token? This indicates the usage of JWTs.

JSON Web Tokens (JWTs)

For those who are not familiar with the concept of JWTs, here's a quick run-through. For a more detailed explanation on JWTs, you may visit this page, which goes into much more depth. JWTs are a compact and self-contained method to transmit JSON objects between parties, such as a client and server.


JWT_Diagram

Illustration of JWT

When you successfully login to a Web Application, the server will generate a JWT for that specific login session and send it to the client in the Response. The server does so by setting a header, known as the authorization header, with the word "Bearer" concatenated with the value of the JWT.

From this point on, everytime the client requests access to any resource on the server, the request must contain this authorization header containing the JWT. Otherwise, access will be denied to the client. However, like in this case, simply having a valid JWT may not be enough to be granted access to a resource.


JWT_Claims

Illustration of Client/Server communication with JWTs

JWTs contain something known as "claims". Claims are basically a key/value pair that is contained within the payload of JWT. JWTs are typically not encrypted, and are simply in base64, allowing us to easily read what's inside any typical JWT we receive. Encryption is not really a concern because any sensitive data are not supposed to be stored within a JWT in the first place. Additionally, JWTs contain a Signature, and any modifications made to a JWT will invalidate it, which will cause the server to reject it.

The JWT that we received from the server can be seen to have a claim for the user's role. We can therefore infer that this value is what they use to perform their Role Based Access Control. It is clear that we need to modify this claim and somehow get the server to still think that the Signature is valid.


JWT_Decoded

Contents of the JWT received from the login page of the Challenge

If we simply modify the JWT, the Signature will be invalidated.


JWT_Signature

Contents of the JWT received from the login page of the Challenge
Vulnerability

We decided to try inspecting the element of the login page, and noticed this little gem.


login_element

Comment within the body of the page

This was surely a hint. Alright, so now we know the vulnerability had something to do with the Public Key used to sign the JWT. This led to us wondering; How do we get the Public Key? An idea popped into our heads; What if we just tried to access the resource directly on the website? We entered http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41031/public.pem and promptly received a download for their public key.

Now we had the key, but we still had no idea what to do with it. After doing some research, we discovered that JWTs indeed had a few vulnerabilities that could be exploited. More specifically, we would be exploiting the signing algorithm defined in the header. Notice how the JWT we decoded from the login page shows that RS256 was used to sign it.

RS256 utilizes RSA with SHA-256 to sign the token. Typically, the signing (encryption) is done by using the Private Key of the server, while the verification (decryption) of the JWT is done by using the Public Key of the server. Meanwhile, another algorithm supported by JWT, known as HS256, utilizes HMAC with SHA-256 to sign the JWT. The main difference is that HS256 uses Symmetric Key Encryption. This means that both its signing and verification are done with one single key. This will be the key to the exploit.

If the server does not check what algorithm was used to initially sign the JWT, another algorithm can be specified for the server to use when verifying it. Therefore, if we change the algorithm in the header to HS256, the server will attempt to use its Private Key to verify the JWT instead of using its Public Key. This is because the server signed the JWT with its Private Key, and since it believes the algorithm was HS256, it will use the same key to verify the JWT.

All we have to do is create a token and sign it with the Public Key that we received earlier from the server.

Exploit

We found a video explaining and showcasing the exploit and made the following Python Script to generate our forged token.

import base64, hmac, hashlib

with open("public.pem", 'r') as f:
    pubkey = f.read()

header = '{"alg":"HS256"}'
payload = '{"user":"minion","role":"admin","iat": 1607098377}'

headerBytes = base64.urlsafe_b64encode(header.encode('utf-8'))
encodedHeader = str(headerBytes, "utf-8").rstrip("=")

payloadBytes = base64.urlsafe_b64encode(payload.encode("utf-8"))
encodedPayload = str(payloadBytes, "utf-8").rstrip("=")

token = (encodedHeader + "." + encodedPayload)

signature = base64.urlsafe_b64encode(hmac.new(bytes(pubkey, "UTF-8"), token.encode("utf-8"),hashlib.sha256).digest()).decode("UTF-8").rstrip("=")

jwt = token+"."+signature
print(jwt)
Enter fullscreen mode Exit fullscreen mode

With that, we ran the script and obtained our forged JWT.


forged_jwt

Forged JWT

We used Burpsuite to set this as our JWT in the Response received from the login page.


burp_intercept_2

Intercepted Response from the login page with the newly forged JWT

Voilà, we have successfully logged in and received the flag, govtech-csg{5!gN_0F_+h3_T!m3S}.


successful_login

Successfully logged in and received the flag

Conclusion

JWTs may be a basic concept to those with experience in the Web Industry, and this challenge may seem easy, but it took us hours of thinking, experimenting, researching, and maybe some whisky, to crack. Overall, it was a really fun and interesting experience! The satisfaction of completing a challenge that you took hours to solve is hard to beat.

If you found the challenge to be...well...challenging, don't worry, we did too. Everyone starts from somewhere, and each time you fail, you learn something new. Participating in more CTFs and similar events help expose you to many things you would never have known or seen, as well as occasionally introducing you to some great new tools!

The point is, don't give up, and you'll reach your goals one day! I hope you enjoyed reading this write up and found it to be informative and well-written.

Resources

By Bacon Potato Cat

Top comments (0)