JWT header parameter injections

JWT header parameter injections

According to the JWS specification, only the alg header parameter is mandatory. In practice, however, JWT headers (also known as JOSE headers) often contain several other parameters. The following ones are of particular interest to attackers.

  • jwk (JSON Web Key) - Provides an embedded JSON object representing the key.

  • jku (JSON Web Key Set URL) - Provides a URL from which servers can fetch a set of keys containing the correct key.

  • kid (Key ID) - Provides an ID that servers can use to identify the correct key in cases where there are multiple keys to choose from. Depending on the format of the key, this may have a matching kid parameter.

Injecting self-signed JWTs via the jwk parameter

The JSON Web Signature (JWS) specification describes an optional jwk header parameter, which servers can use to embed their public key directly within the token itself in JWK format.

JWK

A JWK (JSON Web Key) is a standardized format for representing keys as a JSON object.

You can see an example of this in the following JWT header:

{
    "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
        "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
    }
}

Ideally, servers should only use a limited whitelist of public keys to verify JWT signatures. However, misconfigured servers sometimes use any key that's embedded in the jwk parameter.

You can exploit this behavior by signing a modified JWT using your own RSA private key, then embedding the matching public key in the jwk header.

Although you can manually add or modify the jwk parameter in Burp, the JWT Editor extension provides a useful feature to help you test for this vulnerability:

  1. With the extension loaded, in Burp's main tab bar, go to the JWT Editor Keys tab.

  2. Send a request containing a JWT to Burp Repeater.

  3. In the message editor, switch to the extension-generated JSON Web Token tab and modify the token's payload however you like.

  4. Click Attack, then select Embedded JWK. When prompted, select your newly generated RSA key.

  5. Send the request to test how the server responds.

You can also perform this attack manually by adding the jwk header yourself. However, you may also need to update the JWT's kid header parameter to match the kid of the embedded key. The extension's built-in attack takes care of this step for you.

Lab

This lab uses a JWT-based mechanism for handling sessions. The server supports the jwk parameter in the JWT header. This is sometimes used to embed the correct verification key directly in the token. However, it fails to check whether the provided key came from a trusted source.

To solve the lab, modify and sign a JWT that gives you access to the admin panel at /admin, then delete the user carlos.

You can log in to your own account using the following credentials: wiener:peter

Steps

Get the JWT Token

  • Install JWT Editor extension in burp suite

  • Visit the website and login as wiener and password as peter

  • send the request which contains the jwt token to repeater

Sign and generate RSA key

  • Go to the JWT Editor Keys tab.

  • Generate a new RSA key

  • In the repeater tab change the sub value to administrator in the payload section

  • Click on the Attack button

  • Click on Ok

  • Now copy the newly created JWT Token

Delete the user Carlos

Injecting self-signed JWTs via the jku parameter

Instead of embedding public keys directly using the jwk header parameter, some servers let you use the jku (JWK Set URL) header parameter to reference a JWK Set containing the key. When verifying the signature, the server fetches the relevant key from this URL.

JWK Set

A JWK Set is a JSON object containing an array of JWKs representing different keys. You can see an example of this below.

{
    "keys": [
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
            "n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
        },
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
            "n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
        }
    ]
}

JWK Sets like this are sometimes exposed publicly via a standard endpoint, such as /.well-known/jwks.json.

More secure websites will only fetch keys from trusted domains, but you can sometimes take advantage of URL parsing discrepancies to bypass this kind of filtering. We covered some examples of these in our topic on SSRF.

Lab

This lab uses a JWT-based mechanism for handling sessions. The server supports the jku parameter in the JWT header. However, it fails to check whether the provided URL belongs to a trusted domain before fetching the key.

To solve the lab, forge a JWT that gives you access to the admin panel at /admin, then delete the user carlos.

You can log in to your own account using the following credentials: wiener:peter

Steps

Get the JWT Token

  • Install JWT Editor extension in burp suite

  • Visit the website and login as wiener and password as peter

  • send the request which contains the jwt token to repeater

generate RSA key

  • Go to the JWT Editor Keys tab.

  • Generate a new RSA key

Upload the JWK key to Exploit server

{ "keys": 
  [
     {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "a68d162b-2995-43e7-9e6e-e23d5eead1ec",
        "n": "pbt7QL5_F4PviT21LF8FYC1kTYz40bgzqdtc6Ioha2kOeE0CNnQr9ruEHR2E5rAmzkkKavOoMydXdb_X4epVC-9wSvni-S_wyYlhO1eRSner3uLzDh0ZWZUcuaFVNa4nuIKixc3bQh-DxH5AsYJ1De80-q7wAWTNkkVjIXmLz8ePXzrvbXZAO_YHKgkFEGJQzNSBCp9Sy2eKhpI-1b-Nqngt2NIeu7EiOK9VzwjJXgArouqsHP_PV_S3jP2oepHKSZupz1AfrEjU5mjsUXxg9_p9IJ3XeGynyCz_YbCwGLN7ZHfaAUgs5_bfsmc1zog-HwYK17RKSsOqR_P1LvPotQ"
     }
  ]
}
  • Now copy the given url from exploit server.

Sign the add jku parameter

  • add jku paramter with the given exploit server url in the header section

  • Change the sub value in Payload tab to administrator

  • Click on the sign button

  • Select Update/Generate "alg", "typ", and "kid" parameters

  • Click Ok

  • Copy the new JWT

Delete the user Carlos

Injecting self-signed JWTs via the kid parameter

Servers may use several cryptographic keys for signing different kinds of data, not just JWTs. For this reason, the header of a JWT may contain a kid (Key ID) parameter, which helps the server identify which key to use when verifying the signature.

Verification keys are often stored as a JWK Set. In this case, the server may simply look for the JWK with the same kid as the token. However, the JWS specification doesn't define a concrete structure for this ID - it's just an arbitrary string of the developer's choosing. For example, they might use the kid parameter to point to a particular entry in a database, or even the name of a file.

If this parameter is also vulnerable to directory traversal, an attacker could potentially force the server to use an arbitrary file from its filesystem as the verification key.

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

This is especially dangerous if the server also supports JWTs signed using a symmetric algorithm. In this case, an attacker could potentially point the kid parameter to a predictable, static file, then sign the JWT using a secret that matches the contents of this file.

You could theoretically do this with any file, but one of the simplest methods is to use /dev/null, which is present on most Linux systems. As this is an empty file, reading it returns an empty string. Therefore, signing the token with a empty string will result in a valid signature.

If you're using the JWT Editor extension, note that this doesn't let you sign tokens using an empty string. However, due to a bug in the extension, you can get around this by using a Base64-encoded null byte.

If the server stores its verification keys in a database, the kid header parameter is also a potential vector for SQL injection attacks.

Lab

This lab uses a JWT-based mechanism for handling sessions. In order to verify the signature, the server uses the kid parameter in JWT header to fetch the relevant key from its filesystem.

To solve the lab, forge a JWT that gives you access to the admin panel at /admin, then delete the user carlos.

You can log in to your own account using the following credentials: wiener:peter

Steps

  • Install JWT Editor in burp suite

  • Visit the website and login as wiener:peter

  • Visit the /admin route and send the request to repeater

  • Now, generate a new symmetric key by clicking on the JWT Editor Tab

  • Select Specify secret option and click on generate

  • In the below section change the "k" value to "AA==" which is base64 null byte

  • Click Ok

  • In the repeater tab select the JSON Web Token tab

  • Change the kid value to "../../../.../../dev/null"

  • Change the sub value to administrator

  • Click on the Sign button and click Ok

  • Copy the newly generate JWT value

  • Visit the /admin/delete?username=carlos route

  • In the proxy tab change the JWT value to the new JWT value

  • Forward the request

  • $$$

Other interesting JWT header parameter

The following header parameters may also be interesting for attackers:

  • cty (Content Type) - Sometimes used to declare a media type for the content in the JWT payload. This is usually omitted from the header, but the underlying parsing library may support it anyway. If you have found a way to bypass signature verification, you can try injecting a cty header to change the content type to text/xml or application/x-java-serialized-object, which can potentially enable new vectors for XXE and deserialization attacks.

  • x5c (X.509 Certificate Chain) - Sometimes used to pass the X.509 public key certificate or certificate chain of the key used to digitally sign the JWT. This header parameter can be used to inject self-signed certificates, similar to the jwk header injection attacks discussed above. Due to the complexity of the X.509 format and its extensions, parsing these certificates can also introduce vulnerabilities. Details of these attacks are beyond the scope of these materials, but for more details, check out CVE-2017-2800 and CVE-2018-2633.

Last updated