// TECHNOLOGIES / JWT & OAUTH

JWT and OAuth

Your tokens, forged and replayed.

Whether the tokens come from Auth0, Okta, Cognito, Keycloak or fifty lines of your own middleware, the failure modes are the same and they are all catastrophic: a signature the server doesn't really check, a secret that's in every wordlist, a header that tells the server where to fetch its keys. NewScan forges a token and sends it — the finding is the server accepting it, not a claim about the algorithm. Then it does the same for the flow that issued it: an unregistered redirect_uri is only recorded when the server really hands over a code.

Token forgery, proven by acceptance

// tools: test_jwt_forgery · test_jwt_jku · test_jwt_kid

Every forgery check runs against a genuinely protected endpoint — one that returns 200 with a valid token and 401 without. That differential is the false-positive guard: if the endpoint is public, there is nothing to bypass and nothing is recorded. Then the forged token goes in, and the finding is whether it works.

// ALG:NONE ACCEPTANCE  critical

{"alg":"none","typ":"JWT"} {"sub":"1","role":"admin"} . # empty signature -> 200 # the server never verified anything

// RS256 → HS256 CONFUSION  critical

# public key auto-discovered from common paths {"alg":"HS256"} signed with the RSA public key -> 200 # verifier trusted the header's alg

The public key is public. If the verifier lets the token choose the algorithm, anyone holding that key can mint admin tokens.

// JKU / X5U INJECTION  critical

{"alg":"RS256","jku":"http://<canary>/jwks"} # confirmed when the server FETCHES our key set

A server that fetches its verification keys from a URL in the token will verify a signature made with your key. Confirmed out-of-band on the callback — it is both token forgery and SSRF.

// KID PATH TRAVERSAL  critical

{"alg":"HS256","kid":"../../dev/null"} # sign with the empty/known key file, then check # whether the forged token is accepted

Pointing kid at a file whose contents the attacker knows makes the signing key knowable. Guarded so a rejected token never counts.

// WEAK SIGNING SECRET  critical — tool: crack_jwt_secret

Any HMAC-signed token captured during the scan is run against a common-secret wordlist that includes the development placeholders teams forget to rotate. A successful crack is not a warning about entropy — it means the scanner can now mint arbitrary tokens for any user or role, and the finding says so with the recovered secret as evidence.

HS256 token captured -> secret = "secret123" # forge any identity: role=admin, sub=<any user>

Token hygiene — recorded honestly

// tool: inspect_jwt

Every token the scan sees is decoded and checked for a missing exp, a weak symmetric algorithm, and an alg the server should never accept. These are recorded as observations, not findings — reading a token header proves how it is built, not that the server is exploitable, and inflating that into a finding would be a false positive. When the forgery checks above turn one of those observations into a working bypass, then it becomes a finding, with the accepted request attached.

The OAuth2 / OIDC flow itself

// tool: test_oauth_config

Point it at the authorization endpoint — /oauth/authorize, or the authorization_endpoint advertised in .well-known/openid-configuration — and it tests the lever behind the best-known account-takeover chains of recent years.

// REDIRECT_URI NOT VALIDATED  critical — VERIFIED

The check is deliberately strict about what counts. Not "the endpoint reflected our URI", not "the parameter looked unvalidated" — a real redirect whose Location points at the attacker host and carries a code or token. That is the whole takeover: an attacker crafts an authorization link, your server steers the credential to a host they own, and they exchange it.

GET /oauth/authorize?response_type=code&client_id=… &redirect_uri=https://attacker.example/cb&state=… -> 302 Location: https://attacker.example/cb?code= # unregistered URI, real code issued -> verified

// IMPLICIT FLOW STILL ENABLED  observation

response_type=token -> access token in the URL fragment

Tokens in the fragment leak through browser history and Referer headers. Recorded as an observation with the same recommendation every standards body now gives: authorization code plus PKCE.

// AUTHENTICATE, THEN KEEP GOING

oauth_token obtains a real access token (password, client_credentials or refresh_token grant) and harvest_auth captures one from a login endpoint. verify_session notices when it expires mid-scan and re-authenticates. That is what makes the authenticated half of the scan possible — object-level access control, privilege escalation and the access matrix all need a live identity.

The credentials around the token

// tools: test_default_credentials · test_user_enumeration · test_predictable_tokens
Finding How NewScan confirms it Severity
Default credentials A shipped username/password pair authenticates and returns a usable session. critical
Unauthorized password change A cross-user password write succeeds, then the victim's new password logs in — account takeover. critical
Predictable tokens Issued session or reset tokens are sequential or low-entropy across samples. high
User enumeration A login or password-reset differential distinguishes a real account from a fake one. medium
Weak password policy Registration or reset accepts a trivially weak password. medium

How it's recorded

// audit-ready evidence

CATEGORY

Forgery and flow takeover map to OWASP API2 / A07; enumeration to CWE-204; every item carries its CWE into the report.

SEVERITY

One calibrated severity per finding that translates into each framework's risk rating and remediation SLA — no inflated numbers.

EVIDENCE

The forged token, the request that carried it, and the accepting response — an assessor can replay the whole thing from the report.

Point it at your login.

NewScan is free and self-hosted — bring your own key. Everything here runs in the deterministic floor with no model or provider key; only the jku confirmation needs an OOB collaborator. Pass credentials up front and the authenticated checks run in the same pass.