Barbhack 2026: Casino
Recon
I do not remember the exact name of this challenge but it was a casino platform that allows someone to play blackjack, roulette, and poker. The main page tells us that we have 1000 tokens and we need 200 million to claim the flag. It would be complicated to go the gambling route. So we have to go the pwn route.
Identifying endpoints
When we open up Burp Suite and look at the HTTP requests we make, we see that there is an API endpoint for every action we do:
GET /api/new
POST /api/flag
POST /api/poker/deal
POST /api/poker/
(this is from memory, those names might not be exact).
When we load the page for the first time, or when we want to reset our token counter to 1000, we make a GET request to the /api/new endpoint and the server answers with a token generated on their side. This token is in JWT format (JSON Web Token), and it is used by the server to identify our session.
The /api/flag endpoint is the one we’re most interested in because it should give us the flag. When we look at the data sent via the POST request there, we see that we send our token and the server responds with insufficient funds if we ask the flag when our balance is too low.
JWT token inspection
Let’s take a closer look to that token the server gave us. Here’s an example token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJndWVzdCIsImJhbGFuY2UiOjEwMDAsImlhdCI6MTc4ODA0MDAzNH0.N9GpsofLX830F_BwZ8fFKgUGgdkQgJyyfeKMpwROjGM
We can see that it is made up of three parts, separated by a dot ‘.’ and the parts are encoded in base64.
According to the specification, the fields are, in order:
- the header: it gives us the type of the token and the signing algorithm used.
- the payload: it contains the information the server wants to keep about us.
- the signature: this field is calculated by concatenating the header and payload in base64 and signing it using the server’s private key using the cryptographic algorithm specified in the header.
In our case, we can run each field in base64 to decode them to JSON:
$ cat token.jwt | cut -d '.' -f '1' | base64 -d
{"alg":"HS256","typ":"JWT"}%
$ cat token.jwt | cut -d '.' -f '2' | base64 -d
{"sub":"guest","balance":1000,"iat":1788040034}%
Of course, we can’t run the 3rd field in base64 -d because it’s a cryptographic signature and it serves no purpose to do that here.
The “iat” field in the payload stands for “Issued At” and it is a UNIX timestamp for the time the token was generated at. Our balance is a field in the payload. By the way I’m calling these “fields” but they’re really called “claims” in the specification.
So we see here that the claim we have to change is the balance. However if we touch any byte in the payload or the header and send it back to the server with the same signature, the server will know something is up because it will compute that signature again and see that it is not the same, therefore the contents have been tampered with.
This is the whole point of the JWT token thing.
We can’t modify the token like this, so we have to find a way…
JWT token forgery
One attack we can try on the server is to change the signing algorithm from “HS256” to “none”, and leave the signature out, so we only have 2 fields (header and payload). However I tried this and the server didn’t accept to throw away the singing process.. So there must be another way.
HS256 is a symmetric hashing algorithm that uses a single secret key, both for generating the signature and validating it, contrary to the RS256 algorithm that uses a private/public keypair (asymmetric algorithm).
Hashcat can be used to crack this using a wordlist, in this case we’ll try with the infamous rockyou.txt:
$ hashcat -m 16500 -a 0 jwt.txt rockyou.txt
Within a few seconds we find the secret key used to sign the token: jackpot.
We can now use an online tool like jwt.io to sign a modified version of the token, with a big balance, using the secret key we found.
Now we can make a POST request to /api/flag with our new forged token and get the flag: brb{REDACTED}.