agentsclimarketplace

Jwt misconfig

Skill ShulkwiSEC/bb-huge/skills/curated/jwt-misconfig

bb-huge πŸ€— , Personal bug bounty findings hub and bug bounty orchestration for multiple agents

Install
npx -y skills add ShulkwiSEC/bb-huge --skill jwt-misconfig

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 18 stars18 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when testing JWT-based authentication for algorithm confusion, alg:none bypass, weak HMAC secrets, missing expiration, kid parameter injection, and token storage in localStorage. Trigger on: Authorization: Bearer tokens, JWTs in cookies, any base64url encoded header.payload.signature pattern, OAuth2 access tokens, API authentication tokens, SSO tokens, JWKS endpoints. Detects RS256β†’HS256 confusion, public key as HMAC secret, unverified kid values used in file reads or SQL queries, and JWT cracking with short secrets.

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

5.5 KB, as published. Nobody here has run it

JWT Misconfiguration

What Is Broken and Why

JWTs that accept alg: none, trust the algorithm declared in the token header, or use guessable HMAC secrets allow attackers to forge arbitrary tokens β€” including admin-level claims β€” without knowing any signing key. Algorithm confusion attacks exploit servers that accept both RS256 (asymmetric) and HS256 (symmetric): the attacker signs with the public key (which is public) using HS256 and the server verifies it as a valid HMAC.

Key Signals

  • Authorization: Bearer <base64url>.<base64url>.<base64url> in requests
  • JWTs stored in cookies (check httpOnly, Secure, SameSite flags)
  • JWKS endpoint at /.well-known/jwks.json or /oauth/certs (exposes public key)
  • alg field in JWT header β€” watch for HS256, RS256, none
  • kid (Key ID) field in JWT header β€” check for path traversal or SQLi
  • Short or dictionary-based HMAC secret (crack with hashcat)

Methodology

  1. Decode the JWT header and payload (base64url decode each part).
  2. Note the alg value; attempt to change it to none and remove signature.
  3. If alg is RS256, fetch the public key from JWKS endpoint; re-sign with HS256 using the public key as the HMAC secret.
  4. If alg is HS256, attempt to crack the secret with hashcat or jwt_tool.
  5. Check kid value β€” test for path traversal (../../dev/null), SQLi, or SSRF.
  6. Modify exp claim to far-future timestamp; attempt to use expired tokens.
  7. Escalate claims: change role, admin, sub, userId in payload after forging.

Payloads & Tools

# jwt_tool β€” Swiss army knife for JWT attacks
pip install jwt_tool
jwt_tool TOKEN -X a           # alg:none attack
jwt_tool TOKEN -X s           # algorithm confusion (RS256β†’HS256)
jwt_tool TOKEN -C -d wordlist.txt  # crack HMAC secret

# hashcat JWT cracking
hashcat -a 0 -m 16500 TOKEN wordlist.txt

# Manual alg:none
# 1. Decode header: {"alg":"RS256","typ":"JWT"}
# 2. Change to:     {"alg":"none","typ":"JWT"}
# 3. Re-encode and append empty signature: header.payload.
# (trailing dot is required)

# kid SQLi
{"kid": "' UNION SELECT 'attacker_secret' --", "alg":"HS256"}
# Sign token with 'attacker_secret'

# kid path traversal (sign with empty string)
{"kid": "../../dev/null", "alg":"HS256"}
# /dev/null reads as empty β†’ sign with empty string ""

Bypass Techniques

AttackTechnique
alg: noneChange header alg to none, drop signature, keep trailing dot
Algorithm confusionFetch RS256 public key; use it as HS256 HMAC secret
Weak secretCrack short/dictionary HMAC with hashcat -m 16500
kid path traversalPoint kid to /dev/null or known empty file; sign with ""
kid SQLiInject SQL into kid to return attacker-controlled key from DB
Missing expIf no expiry check, reuse old tokens indefinitely
jku/x5u injectionPoint to attacker-hosted JWKS to supply own public key
Embedded JWKInject jwk into header containing attacker's own public key

Exploitation Scenarios

Algorithm confusion to admin: Setup β†’ API uses RS256; JWKS endpoint public at /.well-known/jwks.json. Trigger β†’ Fetch public key β†’ re-sign token with HS256 using public key as secret β†’ set "role":"admin" in payload. Impact β†’ Full admin access without any private key.

alg:none on misconfigured library: Setup β†’ Old version of JWT library doesn't reject alg: none. Trigger β†’ Set alg: none, modify sub to another user's ID, remove signature. Impact β†’ Arbitrary account takeover.

kid path traversal to RCE: Setup β†’ kid is used to load key from filesystem without sanitization. Trigger β†’ kid: "../../proc/self/fd/0" with socket input; or ../../tmp/evil. Impact β†’ Attacker controls signing key; full token forgery.

False Positives

  • alg: none rejected with 401 β€” library properly validates algorithm.
  • RS256 server that only accepts RS256 (not HS256) β€” algorithm confusion not applicable.
  • JWKS endpoint present but server uses pinned key in code β€” jku/x5u injection blocked.

Fix Patterns

// Always whitelist algorithm β€” never derive from token header
jwt.verify(token, secret, { algorithms: ['HS256'] });

// For RS256: pin the public key in code, don't trust jku/jwk headers
jwt.verify(token, publicKeyPem, { algorithms: ['RS256'] });

// Always validate exp
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256'],
  ignoreExpiration: false  // default false β€” make it explicit
});

Related Skills

JWT attacks are a class of [[auth-bypass]] specific to token-based systems. kid SQLi chains into [[sql-injection]]; kid path traversal chains into [[path-traversal]]. Weak session management after JWT compromise connects to [[cookie-attacks]] and [[session-fixation]].

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.