Application Security Published Nov 4, 2024

Exploiting a Weak JWT Signing Secret: A Case Study

A production JWT signing key that still read "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET." Hashcat did the rest. Full account impersonation followed in minutes.

Exploiting a Weak JWT Signing Secret

During our team's comprehensive security assessment for a client, we stumbled upon a significant vulnerability hiding within the authentication process. The core issue? The secret key used to sign the JWT tokens was so alarmingly predictable that it could have been mistaken for a mere placeholder.

This weakness is a goldmine for potential attackers, opening the door to brute-force attacks using tools like Hashcat. The troubling part was that the key we uncovered was a glaring red flag:

"THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING"

Before diving into the specifics, let's take a moment to understand the fundamentals of JWTs and their role in web security.

JSON Web Tokens

JWTs are an open, standardised format for sending cryptographically signed JSON data across systems. These tokens typically contain claims (information about a user or session) and are commonly used for authentication, session handling and access control. Unlike traditional session tokens, JWTs store all necessary data client-side, making them ideal for distributed applications where users interact with multiple back-end services.

JWT Structure

A JWT is composed of three distinct parts, each with a specific purpose:

  • Header: metadata about the token, such as the signing algorithm.
  • Payload: claims and information about the user.
  • Signature: verifies the integrity of the header and payload.
How HMAC signs a JWT header and payload
eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9.
eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm9sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ.
SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9Zd...

Exploiting JWT Vulnerabilities

We identified a weak JWT signing secret at the entry points for Admin and user roles, impacting the client's entire authentication stack. This flaw is classified as an insecure JWT signing issue because the key is weak or easily guessable. An attacker can craft a JWT with arbitrary claims and sign it using the weak key, bypassing authentication measures and gaining unauthorised access to sensitive areas of the application.

Through this exploitation, attackers could forge valid JWTs and escalate to full account takeover. The shocking realisation was that the JWT secret used for signing was nothing more than the aforementioned placeholder text.

Capturing the JWT from the SSO Process

We started at the client's SSO entry point, http://sso.redacted.com/sso-oneasop, which redirected to a hosted Cognito login page at https://redacted.auth.ap-southeast-2.amazoncognito.com/login. After authenticating with valid credentials, we used Burp Suite to capture the request traffic and located the request carrying the Authorization header with the JWT attached.

Captured initial JWT from Burp Suite
Burp Suite showing the Authorization header containing the JWT

Brute-Forcing the Signing Secret

To brute-force the JWT secret, we used Hashcat. The command template:

hashcat -a 0 -m 16500 <JWT_TOKEN_HERE> <PASSWORD_LIST_HERE>

Mode 16500 targets HMAC-SHA256 JWT signatures. A well-chosen wordlist finishes weak keys in seconds rather than weeks.

Hashcat output revealing the cracked JWT secret

Forging a New JWT

Once the secret was cracked, we moved to jwt.io to forge a new token. We pasted the original JWT into the debugger, located the unique_name claim in the payload, and modified it to point at another user, for example, changing 57575 to 57574. With the cracked secret inserted into the signature field, the tool re-signed the token into a valid JWT for a different identity.

Forged JWT at jwt.io with modified unique_name claim

Back in Burp Suite, we replaced the original JWT in the Authorization header with the forged one and re-sent the request. The application accepted it, granting unauthorised access to the target resource as the impersonated user.

Burp Suite request with forged JWT: impersonating another user

Conclusion

JWTs provide a robust framework for authentication in distributed environments, but their security hinges entirely on implementation. A weak signing key is a single-point-of-failure for the entire authentication model. The fix is straightforward: generate a strong, high-entropy secret, rotate it on a schedule, prefer asymmetric algorithms like RS256 where appropriate, and actively monitor for anomalies in token usage.

What innovative strategies do you think are essential to protect accounts and data against token-forgery attacks? Let us know on LinkedIn.