JWT

Introduction to JWT

JSON web tokens (JWTs) are a standardized format for sending cryptographically signed JSON data between systems. They typically send user information for authentication, session handling, and access control. Unlike classic session tokens, all necessary server data is stored client-side within the JWT.

A JWT consists of 3 parts: a header, a payload, and a signature. These are each separated by a dot.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

The header and payload parts of a JWT are base64url-encoded JSON objects.

JWT signature

The server issuing the token generates the signature by hashing the header and payload, sometimes encrypting the resulting hash. This process uses a secret signing key, allowing servers to verify the token's integrity:

Any change to the header or payload results in a mismatched signature.

Without the server's secret signing key, generating a correct signature for a given header or payload is impossible.

  • By design, servers don't store information about the JWTs they issue. Each token is a self-contained entity.

  • JWT attacks involve users sending modified JWTs to the server to achieve malicious goals.

Arbitrary signatures

Sometimes, developers decode tokens without verifying the signature.

So, tamper the jwt and ignore the signature.

No signature

The JWT header contains an alg parameter.

JWTs can be left unsigned (alg set to none). Servers usually reject unsigned tokens, but obfuscation (mixed capitalization) can bypass filters.

Note: even if unsigned, the token's payload must end with a trailing dot.

Tip: Use JSON Web Tokens Burp Extension. Go to the request -> JSON Web Tokens and test "Alg None Attack".

Brute-forcing secret keys

Some signing algorithms, such as HS256 (HMAC + SHA-256), use a string as the secret key -> crack it.

Wordlist: https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list

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

With the key, you can alter the JWT and sign.

JWT header parameter injections

JWT header (JOSE headers)

According to the JWS specification, only the alg header parameter is mandatory. However, JWT headers often contain additional parameters of interest to attackers:

  • jwk (JSON Web Key): An embedded JSON object representing the key.

"jwk": {
    "kty": "RSA",
    "e": "AQAB",
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
  • jku (JSON Web Key Set URL): A URL for servers to fetch the correct key set.

"jku": "https://example.com/.well-known/jwks.json"

https://example.com/.well-known/jwks.json

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "1234567890",
      "use": "sig",
      "n": "modulus_value_here",
      "e": "AQAB"
    }
  ]
}
  • kid (Key ID): An ID for servers to identify the correct key among multiple keys.

Injecting self-signed JWTs via jwk

Servers should use a limited whitelist of public keys to verify JWTs. However, misconfigured servers may accept any key in the jwk parameter. So you can sign JWT with your own RSA private key and embedding the matching public key in the jwk header.

Detect/Exploit with JWT Editor Burp extension

  1. In JWT Editor, create new RSA Key

  2. In Burp Repeater -> JSON Web Token tab.

  3. Tamper the data (in exploit phase)

  4. Finally, click on Attack -> Embedded JWK. (you can do it manually but pay attention to match kid)

Note: you can also perform this attack manually by adding the jwk header yourself. So test it even if the token doesn't have jwk header.

Injecting self-signed JWTs via jku

Some servers use the jku header parameter to reference a JWK Set containing the key instead of embedding keys directly with the jwk parameter. Secure sites fetch keys (to verify the signature) from trusted domains, but URL parsing issues can bypass this.

Detect/Exploit with JWT Editor Burp extension

  1. In JWT Editor, create new RSA Key

  2. In Burp Repeater -> JSON Web Token tab.

  3. Create webpage on your exploit server with JWK Set (JSON Web Token tab -> select key -> create JWK Set). [you can also select Copy Public Key and paste inside "keys" array]

{
    "keys": [
        {
            "p": "1w_iGEEXth1HKIhNQdOOfBRiWNIpGccQPhfp4qKmsNo0jLOgqvncXA07Qv4HnSTKRDMQcZPNQctItBPpM35URzOOjMO94QB00xnHRINK9cTfFTFyahOHkdCFXCcpkcv5dQ9q1qRCJi-cCI4_-pD1BlwI18BnCtBy7HledvK__e0",
            "kty": "RSA",
            "q": "vazYFn29raZrk6rOS_hAkJRaGV5JMFQS59wz_FtV7EswVdgCWf_-t2PK6Z21ElWRbYhBeBkrtJimvVp6KFwDOJtkOvBT8plb8aFEcTgfCzdJGF3RAJien85gRIng45gJC_JAJRae1fLvDEbQ4vPt4TU3OIkY2KoQ22rM9q5dnCk",
            "d": "IAYgwDjOddZTad1gntLlQRW1cK8KQrKu3tfOVpcChR18RhO8BU56p7FLRSgvfDJ2BFOa6viI3brFxu1GycaspYnADiR9UJzc3lIaPgkmsN-9Zix3RJ8sRRsIab0-dlYaN24PUZmlocTnzIEDDpUuafaJykluXnnoxnYtCCfktq2NcXYmFJ9Ui6evn6ceKAcWu-Hcd4gTxgQURjxx6jMmnrYsgyp-oGxgOL6T17_KWHal80RkhsQgLMSwhEPU8NQCTdOpy-Ms3rtLMWVpKmtmOlqtFxgf8yvAGWytL8RkB5Yv6KFzsxKqH3cnGAa8ld0rwvFLQ0wXvkkqvdJYmZKX2Q",
            "e": "AQAB",
            "kid": "1413b1ea-1d9d-42c0-b619-9361c28b9fa5",
            "qi": "XRO3NCBUWiwsZ1WEZInxb_5zbz3KlFNJnWK-e1nNDROPU0VRJseZyzAy3fevYQTy0VH-LiNWkV_pqSByx4pPGlIOcMgWTkDQKoFh91H5mjj4DHZ4x7Wn8MCnDEyY52rR5QejeUtmT6jFVqHEhjCHipjxuJh5h7OV5bWfxW8CPyo",
            "dp": "q21tvAem7vPHlPeRHbeVDDLzcfmT6YhT2isVtCIS3UYSPVWx7Jfen0Gsy2nSh-CbmmFZ6i72nkt8WI7GhNVeOKNQLcSZxpCmjt8th99gESgs6qfPm96VYhXlN9-_swf0gOsZLp8gW2_34JoDRafmqHsUUWZ8vJIMCZN1STuW7sE",
            "dq": "qdF6loh1rmd1oXwnv7TAebGZCWV1OaPMWXK5yJMt1qVq3TDMyi98qkzae1cLqyKZVevMUe6XRtX1U0sSW9gluiTGFE7fmjDcNPYiBQwuHyicdQhp-5KpUoK_hh28D4krcFqwO4SJKRycEe3FT6z9qcivbBqy-CkrdoekgqeSgCE",
            "n": "n1fuIBwePGThDmmsuq0NZ-Gco8KKmsyRbrKm6qusSlgGU-WWh6VPQnTcH-JkQACqPPlVT-gNJODPTSr7jhLVDgks_O9O6wlc8WXfIFDKkLica-NcCY1BgDPir4gy4EHIeKB6_HKF5RTcfjcpTI8q4lMiRIHnxVjD9rVhEPsiL1kv_9F2lRKvbLmxo0O0nPocWTbmvxmN4w-P6CXwpx4dFmebAxKkjRIs_OrqpKQ2UTJns8GW8ETJfZLxErvCS300DWV-0EGsiDlYCDluGK4nt3jfFgilqZUn6SsYWFNTeBT6X2493gRZIB0_hwzdFW8cTNmoa-OlYxUlLikONJUW9Q"
        }
    ]
}
  1. Tamper the data (in exploit phase)

  2. Add a new jku parameter to the header and set its value to the URL of your JWK Set on the exploit server.

  3. Sign and update kid parameter

Tip: to see if the server makes the request, add jku header and insert Burp collaborator.

Note: you can also perform this attack manually by adding the jwk header yourself. So test it even if the token doesn't have jwk header.

Injecting self-signed JWTs via kid

The kid in JWS is an arbitrary string set by the developer, possibly pointing to a database entry or file. If vulnerable to directory traversal, you could force the server to use any file as the verification key.

{
    "kid": "../../path/to/file",
    "typ": "JWT",
    "alg": "HS256",
    "k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}

If the server supports JWTs signed with a symmetric algorithm, you could point the kid to a predictable static file, then sign the JWT using a secret that matches the contents of this file. The best way is to use /dev/null (empty file), and sign the JWT with an empty string to create a valid signature.

Detect/Exploit with JWT Editor Burp extension

  1. In Burp Repeater -> JSON Web Token tab.

  2. Modify kid parameter to test path traversal

  3. Sign with empty string

  4. Repeat the process with different path traversal payload

Tip: try also SQL injection

Last updated