JWT (JSON Web Tokens) Attacks

JSON web token format: $HEADER.$PAYLOAD.$SIGNATURE, where each substring is (URL-safe) base64 encoded. These can be passed around as a user cookie, HTTP header, or queried from local storage.

$HEADER and $PAYLOAD are both JSON blobs, but $SIGNATURE is binary data.

Use basenc --base64url and basenc -d --base64url to encode/decode URL-safe base64, rather than the base64 binary. Be sure to strip the trailing = signs!


“None” Attacks

Sometimes servers will also support the NONE signature type, which indicates that no signing is used (so the JWT is then just $HEADER.$PAYLOAD. - note the trailing dot!). If the server allows the none signing method, then it’s often possible to just arbitrarily edit the $PAYLOAD to gain access to other users.

The base64-encoded version of {"typ":"JWT","alg":"none"} is eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.

Public Key Attacks

JWT algorithms can use a server’s public key if alg is HS256. If the public half of the keypair used to sign the JWT is available somehow (for example, if it’s been re-used as the server’s HTTPS certificate), then we can harvest it and use it to forge new JWTs.

The base64-encoded version of {"typ":"JWT","alg":"HS256"} is eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9Cg.

Use the following to generate a signature with the above $HEADER and the PEM-formatted $PUBLIC_KEY_FILE half of the public/private key to validate the JWTs (when alg is RS256):

echo -n "$HEADER.$PAYLOAD" | \
openssl dgst -sha256 -mac HMAC -macopt hexkey:$(cat $PUBLIC_KEY_FILE | xxd -p | tr -d '
') | \
sed -e 's/.*= //' | \
tr -d '
' | \
xxd -p -r | \
basenc --base64url | \
sed -e 's/=*$//'

Brute-Forcing Weak Secrets

If a weak secret (a simple string) is used to sign the JWT token, then it is sometimes possible to brute-force it using JWT-Cracker.