BreakingBankChallengeWrite-Up(Web)-HTBUniversityCTF2024
Omar Mohamed
Thanks for sharing!
بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ

Hello there! Today, I'm going to walk you through solving the Breaking Bank challenge from HTB University CTF 2024.
Before we get started, if you want to try the challenge, you can find it here.
Content
Let's dive in!
Challenge Overview

Initial Recon
First thing I did was check out the website. We got a login and register form:

Made my account and logged in. We have a cryptocurrency platform with Market Overview and a portfolio which my balance is set to zero:


We also got another 2 interesting tabs: friends and Transactions.
Source Code Review
The source code was quite big, so I took it in reverse - how can I get to the flag?
Searched for
flag.txt
in VS code and found this:We see that we can get the flag if we drained
CLCR
coin (set it to 0) of the user with this email: financial-controller@frontier-board.htb
So how can we do that?
Remember we have a transaction page, so simply we can transfer all the amount of this coin to another user, and we get it
In
/src/pages/Transaction.tsx
, found this endpoint: /api/crypto/transaction
Notice it needs a JWT token? Let's check our token in localStorage:

Passing it to jwt.io, we got:
Header:
Body:
We notice this file:
http://127.0.0.1:1337/.well-known/jwks.json
which gets us this:Quick Note: JWKS (JSON Web Key Sets) is a set of keys containing the public keys used to verify any JWT issued by an authorization server. Thejku
(JWK Set URL) header parameter is used to retrieve these keys.
Let's keep it for later and understand the logic first
Verification Function Analysis
(where the good stuff begins)
searching in vscode for
verify
, we got verifyToken
function in /challenge/server/services/jwksService.js
The functionis pretty basic but here are some parts which pop up:
here it checks if
jku
header starts with http://127.0.0.1:1337/
The
kid
must match the KEY_ID
on the server, which we can easily get from our own tokenNow for the interesting part:
it gets some data from the link in
jku
header, which in our token was: http://127.0.0.1:1337/.well-known/jwks.json
Next, the function retrieves the
jwk
whose kid
matches the server's, then passes it to the jwkToPem
function to convert it into a PEM-formatted public key:The
jwkToPem
function ensures the key is of type RSA
, converts its components (n
and e
) to base64url, and creates a PEM public key using Node's crypto
module.This step is important because we'll use a similar approach to craft our own key later.
now lets's summer up what we have so far:
- Goal: Transfer all
CLCR
fromfinancial-controller@frontier-board.htb
to get the flag. - JWT Validation: The server checks the
jku
URL and fetches a key (jwk
) to verify tokens. - Plan: Host a fake key on a controlled server, make the server fetch from it, and forge a token
First Lets make our hosted fake key
I made the following script, Let's take it bit by bit
- Key Generation:
- Generates an RSA key pair (
privateKey
andpublicKey
) for signing and verifying tokens. KEY_ID
is a unique identifier for the key (you get it from your token)
- Generates an RSA key pair (
- JWKS (JSON Web Key Set):
- Converts the
publicKey
to a JWK (JSON Web Key) format. - Adds metadata (
alg
,use
,kid
)
- Converts the
- JWT (JSON Web Token):
- Creates a token with a payload (
email
andiat
). - Custom
header
includes:kid
: Matches the key ID (KEY_ID
) (Make sure to put yours)jku
: URL pointing to the fake JWKS file.
- Signs the token using the private key.
- Creates a token with a payload (
- Output:
- Logs the forged JWT and JWKS to use in the attack.
If you tried this... it will not work, know why?
Remeber the
it has to start with
http://127.0.0.1:1337/
but how can we make it fetch from our server of it has to start with this???
Answer is open redirect
So a little of searching (Also these
// TODO
commenets made it easier to spot the right stuff) we find analytics.js
file with this insideThis is a straight up redirection
so that's how we are gonna make the server fetch our hosted key
You can use ngrok, but I hosted mine on my website:
https://h4ck.run/jwks.json
The final url will look like this:http://127.0.0.1:1337/api/analytics/redirect?url=https://h4ck.run/jwks.json/&ref=dummy
Notice the dummy
ref
is required in order to redirectNote: This script generates its keys dynamically in each run, so make sure to use the right token with its right hosted key
Now since we finally got our token, lets try it out
Getting Access
Now with our forged token, let's check if it worked:

Nice! Our portfolio value isn't zero anymore, which means we got in!
Getting the Flag
Made another account and added it as a friend to transfer the coins to. But when trying to transfer...

Still need to bypass the OTP. Looking at
otpMiddleware.js
:It's using
includes()
!If you send a dummy otp and see the request in burp, you can see the otp is sent in an array

So simply make and array with all possible otps and you got it
["0001","0002","0003","0004","0005"..."9999"]
The command to generate it:
To copy it use:
Note: Put it in burp by deleting the existing array and putting yours. Don't put it directly in the input beacuse it will be handled as one string in this array, as you can see the otp was passed as a string


And here is our flag!
Flag:HTB{rugg3d_pu11ed_c0nqu3r3d_d14m0nd_h4nd5_789dfb9e1ce0f5ca7741ba640da6ce29}
Achievement
I'm proud to share that our team ranked in the top 200 in this CTF! Here's the certificate of achievement:

Key Takeaways
This challenge had a bit of everything:
- JWT and JWKS stuff
- Open redirect vulnerability
- OTP bypass using array tricks
Really enjoyed solving this one! Hope you liked the write-up. See you in the next one!
psst.. you can buy me a coffee here 👀. thank you!
Tags: