JWT Attacks
Last updated
Last updated
JSON web tokens (JWTs) are a standardized format for sending cryptographically signed JSON data between systems.
They can theoretically contain any kind of data, but are most commonly used to send information ("claims") about users as part of authentication, session handling, and access control mechanisms.
Unlike with classic session tokens, all of the data that a server needs is stored client-side within the JWT itself. This makes JWTs a popular choice for highly distributed websites where users need to interact seamlessly with multiple back-end servers.
The header is a base64 encoded json data which typically consists of two parts:
The type of the token, which is JWT,
The signing algorithm being used , such as HMAC SHA256 or RSA.
The payload is also a base64 encoded json data,This data is usually a user info used for Authorization.
📌 The data in the payload is readable,but it’s protected against tampering.
To create the signature you have to take the encoded header, the encoded payload, a secret.
,the algorithm specified in the header, and sign that in the next form:
📌 **The Signature is used as a part of the security validation on that JWT,to make sure no one tampers the data unless he owns a secret key.**
A JWT is usually either a JWS(JSON Web Signature) or JWE(JSON Web Encryption) token.
When people use the term "JWT", they almost always mean a JWS token. JWEs are very similar, except that the actual contents of the token are encrypted rather than just encoded.
JWT libraries typically provide one method for verifying tokens and another that just decodes them. For example, the Node.js library
jsonwebtoken
hasverify()
anddecode()
📌 The mistake that the developers might do is passing the incoming tokens to the `decode()` Method With out verifying them.
📌 Just edit the jwt to what ever content you want and it’s accepted.
The alg
parameter in the header is used to tell the server which algorithm was used to sign the token which he uses while verifying the signature.
📌 JWT Can be left unsigned. In this case, the `alg` parameter is set to `none`.It’s called unsecured JWT —> JWS without a signature
Change The alg parameter value : none
change the payload
remove the signature from the payload(keep the tailed dot)
Some times it’s not allowed to send the JWT with alg : none
📌 bypassed using classic obfuscation techniques, such as mixed capitalization and unexpected encodings.
📌 Some signing algorithms, such as HS256 (HMAC + SHA-256), use an arbitrary, standalone string as the secret key. Just like a password, it's crucial that this secret can't be easily guessed or brute-forced by an attacker.
Wordlist of Common Secret Keys :
https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list
📌 Two algorithms are supported for signing JWTs: **RS256** and **HS256.**RS256 generates an asymmetric signature, which means a private key must be used to sign the JWT and a different public key must be used to verify the signature.
The JSON Web Key Set (JWKS) is a set of keys containing the public keys used to verify any JSON Web Token (JWT) issued by the authorization server and signed using the RS256 signing algorithm
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.
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](https://portswigger.net/web-security/jwt#injecting-self-signed-jwts-via-the-jwk-parameter) 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
public keys are used to verify the jwt,so just edit your token then Embedding your own key
would be enough for exploiting this flaw.
📌 If the token uses a “jku” Header claim then check out the provided URL. This should point to a URL containing the JWKS file that holds the Public Key for verifying the token. Tamper the token to point the jku value to a web service you can monitor traffic for.
JWKS:
Create your own jwk using the private key
Host your public key on your own domain
Go and use burp :
Working with JWTs in Burp Suite | Web Security Academy
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.
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, fetching it returns null. Therefore, signing the token with a Base64-encoded null byte will result in a valid signature.
Create New Symmetric Key
Replace the generated value for the k
property with a Base64-encoded null byte (AA==)
. or any value that matches the concept.
Set The kid
parameter value to ../../../../../../../../dev/null
Use the new Symmetric Key to create your jwt.
📌 Force the server to verify the signature of a ([JWT](https://portswigger.net/web-security/jwt)) using a different algorithm than is intended by the website's developers.
methods rely on the
alg
parameter in the token's header to determine the type of verification they should perform.
EXAMPLE OF A METHOD :
📌 Problems arise when website developers who subsequently use this method assume that it will exclusively handle JWTs signed using an asymmetric algorithm like RS256. Due to this flawed assumption, they may always pass a fixed public key to the method
EXAMPLE OF A FLAW :
The algorithm HS256 uses the secret key to sign and verify each message. The algorithm RS256 uses the private key to sign the message and uses the public key for authentication. If you change the algorithm from RS256 to HS256, the back end code uses the public key as the secret key and then uses the HS256 algorithm to verify the signature.Then, using the public key and changing RS256 to HS256 we could create a valid signature.
📌 The public key you use to sign the token must be absolutely identical to the public key stored on the server. which in this case it’s a SECRET to a symmetric algorithm.
Obtain the server's public key
Convert the public key to a suitable format
Create a malicious JWT with a modified payload and the alg
header set to HS256
.
Sign the token with HS256, using the public key as the secret.
Could be found in an endpoint or leaked somewhere in an endpoint like
/.well-known/jwks.json
/jwks.json
Deriving public keys from existing tokens
Outputs multiple JWT with the key that is being used to validate it,The JWT that works use it’s key to sing your new JWT
📌 Although the server may expose their public key in JWK format, when verifying the signature of a token, it will use its own copy of the key from its local filesystem or database. This may be stored in a different format.
In order for the attack to work, the version of the key that you use to sign the JWT must be identical to the server's local copy.
Or you can just use it as it’s,but base-64 decoded ,try it .
Run jwt_tool with mode All Tests. —> not always working - Nothing is that easy
Check where the token originated in your proxy's request history. It should be created on the server, not the client.
Check if the token lasts more than 24h... maybe it never expires. If there is a "exp" filed, check if the server is correctly handling it.
If the claim "kid" is used in the header, check the web directory for that file or a variation of it. For example if "kid":"key/12345"
then look for /key/12345 and /key/12345.pem on the web root
In a scenario wehre the content of the "kid" is used to retreive the password from the database, you could change the payload inside the "kid" parameter to: non-existent index' UNION SELECT 'Your Secret';--
In a scenario where the "kid" parameter contains a path to the file with the key and this path is being used inside an executed command you could be able to obtain RCE and expose the private key with a payload like the following: /root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&