Token Analysis
Last updated
Last updated
When implemented correctly, tokens can be an excellent tool that can be used to authenticate and authorize users. However, if anything goes wrong when generating, processing, or handling tokens, they can become our keys to the kingdom.
In this section, we will take a look at the process that can be used with Burp Suite to analyze tokens. Using this process can help you identify predictable tokens and aid in token forgery attacks. To analyze tokens we will take our crAPI API authentication request and proxy it over to Burp Suite.
Next, you will need to right-click on the request and forward it over to Sequencer. In Sequencer, we will be able to have Burp Suite send thousands of requests to the provider and perform an analysis of the tokens received in response. This analysis could demonstrate that a weak token creation process is in use.
Navigate to the Sequencer tab and select the request that you forwarded. Here we can use the Live Capture to interact with the target and get live tokens back in a response to be analyzed. To make this process work, you will need to define the custom location of the token within the response. Select the Configure button to the right of Custom Location. Highlight the token found within quotations and click OK.
Once the token has been defined then you can Start live capture. At this point, you can either wait for the capture to process thousands of requests or use the Analyze now button to see results sooner.
Using Sequencer against crAPI shows that the tokens generated seem to have enough randomness and complexity to not be predictable. Just because your target sends you a seemingly complex token, does not mean that it is safe from token forgery. Sequencer is great at showing that some complex tokens are actually very predictable. If an API provider is generating tokens sequentially then even if the token were 20 plus characters long, it could be the case that many of the characters in the token do not actually change. Making it easy to predict and create our own valid tokens.
To see what an analysis of a poor token generation process looks like perform an analysis of the "bad tokens" located on the Hacking APIs Github repository (https://raw.githubusercontent.com/hAPI-hacker/Hacking-APIs/main/bad_tokens). This time around we will use the Manual load option, to provide our own set of bad tokens.
If you use the Analyze Now button and let Sequencer run its analysis. Check out the Character-level analysis which reveals that the 12 alpha-numeric token uses the same characters for the first 8 positions. The final three characters of these tokens have some variation. Taking note of the final three characters of these tokens you can notice that the possibilities consist of two lower-case letters followed by a number (aa#). With this information, you could brute-force all of the possibilities in under 7,000 requests. Then you can take these tokens and make requests to an endpoint like /identity/api/v2/user/dashboard. Based on the results of your requests, search through the usernames and emails to find users that you would like to attack.
JSON Web Tokens (JWTs) are one of the most prevalent API token types because they operate across a wide variety of programming languages, including Python, Java, Node.js, and Ruby. These tokens are susceptible to all sorts of misconfiguration mistakes that can leave the tokens vulnerable to several additional attacks. These attacks could provide you with sensitive information, grant you basic unauthorized access, or even administrative access to an API. This module will guide you through a few attacks you can use to test and break poorly implemented JWTs.
JWTs consist of three parts, all of which are base64 encoded and separated by periods: the header, payload, and signature. JWT.io is a free web JWT debugger that you can use to check out these tokens. You can spot a JWT because they consist of three periods and begin with "ey". They begin with "ey" because that is what happens when you base64 encode a curly bracket followed by a quote, which is the way that a decoded JWT always begins.
Here you can see the example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImhhcGloYWNrZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.U1Agy_OvIULTQwBrYx0dlWVzcBqcI90no2pAcoy4-uo
We can take this JWT and decode the individual parts. The header is the first segment. You can simply echo that part of the token and base64 decode it to see what the header contains:
$ echo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9|base64 -d
{"alg":"HS256","typ":"JWT"}
The algorithm (alg) used for this token is HS256 and the token type (typ) is JWT. Next, we can do the same with the payload:
$ echo eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImhhcGloYWNrZXIiLCJpYXQiOjE1MTYyMzkwMjJ9|base64 -d
{"sub":"1234567890","name":"hapihacker","iat":1516239022}
Now, this token only contains the subject (sub), the username (name), and the time that the token was issued at (iat). More information could be contained in the body, in fact, whatever the provider would like to add here they can. A JWT body can be a great source of information disclosure. Consider a JWT that contains the username, email, password, and role all hiding behind one base64 decode. If we run the same command for the JWT signature we will see the following:
$ echo U1Agy_OvIULTQwBrYx0dlWVzcBqcI90no2pAcoy4-uo|base64 -d
SP base64: invalid input
Finally, the signature is the output of HMAC used for token validation and generated with the algorithm specified in the header. To create the signature, the API base64-encodes the header and payload and then applies the hashing algorithm and a secret. The secret can be in the form of a password or a secret string, such as a 256-bit key. Without knowledge of the secret, the payload of the JWT will remain encoded.
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
If you would like to learn more about JWTs, check out https://jwt.io/introduction.
If you’ve captured another user’s JWT or maybe discovered a leaked token during reconnaissance, you can try sending it to the provider and pass it off as your own. There is a chance that the leaked token has not yet expired and can be passed off as your own. You could use this token in requests to gain access to the API as the user specified in the payload. More commonly, though, you’ll obtain a JWT by authenticating to an API and the provider will respond with a JWT. In order to obtain a JWT from crAPI, we will need to leverage our authentication request.
The token we receive back from crAPI doesn't necessarily say "JWT: anywhere, but we can easily spot the "ey" and three segments separated by periods. The first step to attacking a JWT is to decode and analyze it. If we take this token and add it to the JWT debugger this is what we see.
In this example, we can see the algorithm is set to HS512, the email of our account, iat, exp, and the current signature is invalid. If we were able to compromise the signature secret then we should be able to sign our own JWT and potentially gain access to any valid user's account. Next, we will learn how to use automated tooling to help us with various JWT attacks.
The JSON Web Token Toolkit or JWT_Tool is a great command line tool that we can use for analyzing and attacking JWTs. With this, we will be able to analyze JWTs, scan for weaknesses, forge tokens, and brute-force signature secrets. If you followed along during the setup module you should be able to use the jwt_tool alias to see the usage options:
$jwt_tool
Some of the options to note include:
-h to show more verbose help options
-t to specify the target URL
-M to specify the scan mode
pb to perform a playbook audit (default tests)
at to perform all tests
-rc to add request cookies
-rh to add request headers
-rc to add request cookies
-pd to add POST data
Also, make sure to check out the Wiki for more information https://github.com/ticarpi/jwt_tool/wiki .
To perform a baseline analysis of a JWT simply use jwt_tool along with your captured JWT to see information similar to the JWT Debugger.
As you can see, jwt_tool makes the header and payload values nice and clear. Additionally, jwt_tool has a “Playbook Scan” that can be used to target a web application and scan for common JWT vulnerabilities. You can run this scan by using the following:
$ jwt_tool -t http://target-name.com/ -rh "Authorization: Bearer JWT_Token" -M pb
In the case of crAPI we will run:
$jwt_tool -t http://127.0.0.1:8888/identity/api/v2/user/dashboard -rh "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyYWFhQGVtYWlsLmNvbSIsImlhdCI6MTY1ODUwNjQ0NiwiZXhwIjoxNjU4NTkyODQ2fQ.BLMqSjLZQ9P2cxcUP5UCAmFKVMjxhlB4uVeIu2__6zoJCJoFnDqTqKxGfrMcq1lMW97HxBVDnYNC7eC-pl0XYQ" -M pb
During this scan for common misconfiguration, JWT_Tool tested the various claims found within the JWT (sub, iat, exp)
If you ever come across a JWT using "none" as its algorithm, you’ve found an easy win. After decoding the token, you should be able to clearly see the header, payload, and signature. From here, you can alter the information contained in the payload to be whatever you’d like. For example, you could change the username to something likely used by the provider’s admin account (like root, admin, administrator, test, or adm), as shown here: { "username": "root", "iat": 1516239022 } Once you’ve edited the payload, use Burp Suite’s Decoder to encode the payload with base64; then insert it into the JWT. Importantly, since the algorithm is set to "none", any signature that was present can be removed. In other words, you can remove everything following the third period in the JWT. Send the JWT to the provider in a request and check whether you’ve gained unauthorized access to the API.
There is a chance the API provider isn’t checking the JWTs properly. If this is the case, we may be able to trick a provider into accepting a JWT with an altered algorithm. One of the first things you should attempt is sending a JWT without including the signature. This can be done by erasing the signature altogether and leaving the last period in place, like this: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJoYWNrYXBpcy5pbyIsImV4cCI6IDE1ODM2Mzc0ODgsInVzZ XJuYW1lIjoiU2N1dHRsZXBoMXNoIiwic3VwZXJhZG1pbiI6dHJ1ZX0. If this isn’t successful, attempt to alter the algorithm header field to "none". Decode the JWT, update the "alg" value to "none", base64-encode the header, and send it to the provider. To simplify this process, you can also use the jwt_tool to quickly create a token with the algorithm switched to none.
$ jwt_tool eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyYWFhQGVtYWlsLmNvbSIsImlhdCI6MTY1ODg1NTc0MCwiZXhwIjoxNjU4OTQyMTQwfQ._EcnSozcUnL5y9SFOgOVBMabx_UAr6Kg0Zym-LH_zyjReHrxU_ASrrR6OysLa6k7wpoBxN9vauhkYNHepOcrlA -X a
If successful, pivot back to the None attack. However, if we try this with crAPI, the attack is not successful. You can also use JWT_Tool to create a none token.
A more likely scenario than the provider accepting no algorithm is that they accept multiple algorithms. For example, if the provider uses RS256 but doesn’t limit the acceptable algorithm values, we could alter the algorithm to HS256. This is useful, as RS256 is an asymmetric encryption scheme, meaning we need both the provider’s private key and a public key in order to accurately hash the JWT signature. Meanwhile, HS256 is symmetric encryption, so only one key is used for both the signature and verification of the token. If you can discover and obtain the provider’s RS256 public key then switch the algorithm from RS256 to HS256, there is a chance you may be able to leverage the RS256 public key as the HS256 key. It uses the format jwt_tool TOKEN -X k -pk public-key.pem, as shown below. You will need to save the captured public key as a file on your attacking machine (You can simulate this attack by taking any public key and saving it as public-key-pem).
$ jwt_tool eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyYWFhQGVtYWlsLmNvbSIsImlhdCI6MTY1ODg1NTc0MCwiZXhwIjoxNjU4OTQyMTQwfQ._EcnSozcUnL5y9SFOgOVBMabx_UAr6Kg0Zym-LH_zyjReHrxU_ASrrR6OysLa6k7wpoBxN9vauhkYNHepOcrlA -X k -pk public-key-pem
Once you run the command, JWT_Tool will provide you with a new token to use against the API provider. If the provider is vulnerable, you’ll be able to hijack other tokens, since you now have the key required to sign tokens. Try repeating the process, this time creating a new token based on other API users (other discovered users or generic admin users).
The JWT Crack attack attempts to crack the secret used for the JWT signature hash, giving us full control over the process of creating our own valid JWTs. Hash-cracking attacks like this take place offline and do not interact with the provider. Therefore, we do not need to worry about causing havoc by sending millions of requests to an API provider. You can use JWT_Tool or a tool like Hashcat to crack JWT secrets. You’ll feed your hash cracker a list of words. The hash cracker will then hash those words and compare the values to the original hashed signature to determine if one of those words was used as the hash secret. If you’re performing a long-term brute-force attack of every character possibility, you may want to use the dedicated GPUs that power Hashcat instead of JWT_Tool. That being said, JWT_Tool can still test 12 million passwords in under a minute. First, let's use Crunch, a password-generating tool, to create a list of all possible character combinations to use against crAPI.
$crunch 5 5 -o crAPIpw.txt
We can use this password file that contains all possible character combinations created for 5 character passwords against our crAPI token. To perform a JWT Crack attack using JWT_Tool, use the following command: $ jwt_tool TOKEN -C -d /wordlist.txt The -C option indicates that you’ll be conducting a hash crack attack, and the -d option specifies the dictionary or wordlist you’ll be using against the hash. JWT_Tool will either return “CORRECT key!” for each value in the dictionary or indicate an unsuccessful attempt with “key not found in dictionary.”
Now that we have the correct secret key that is used to sign crAPI's tokens, we should be able can generate our own trusted tokens. To test out our new abilities, you can either create a second user account in crAPI or use an email that you have already discovered. I have created an account named "superadmin".
You can add your user token to the JWT debugger, add the newly discovered secret, and update the "sub" claim to any email that has registered to crAPI.
Use the token that you generated in the debugger and add it to your Postman Collection. Make a request to an endpoint that will prove your access such as GET /identity/api/v2/user/dashboard.
Congrats!