{"id":"CVE-2026-41213","aliases":["EUVD-2026-25272","GHSA-jhm7-29pj-4xvf"],"status":"published","title":"@node-oauth/oauth2-server: PKCE code_verifier ABNF not enforced in token exchange allows brute-force redemption of intercepted authorization codes","description":"@node-oauth/oauth2-server is a module for implementing an OAuth2 server in Node.js. The token exchange path accepts RFC7636-invalid code_verifier values (including one-character strings) for S256 PKCE flows. Because short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force code_verifier guesses online until token issuance succeeds.","severity":{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00259,"percentile":0.1775,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-1289","CWE-307"],"published":"2026-04-16T21:09:50Z","modified":"2026-10-28T02:30:52Z","fixes":[{"ecosystem":"npm","package":"@node-oauth/oauth2-server","fixed_in":["5.3.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"@node-oauth/oauth2-server","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"5.2.1"},{"fixed":"5.3.0"}]}]},{"source":"osv","ecosystem":"npm","package":"@node-oauth/oauth2-server","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"5.3.0"}]}]}],"products":[{"vendor":"node-oauth","product":"node-oauth2-server"},{"vendor":"node-oauth","product":"node-oauth\\/oauth2-server"}],"references":[{"url":"https://github.com/node-oauth/node-oauth2-server/security/advisories/GHSA-jhm7-29pj-4xvf","tags":["x_refsource_CONFIRM","exploit","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41213","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-jhm7-29pj-4xvf"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/41xxx/CVE-2026-41213.json","tags":["advisory"]},{"url":"https://github.com/node-oauth/node-oauth2-server","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-41213","title":"@node-oauth/oauth2-server: PKCE code_verifier ABNF not enforced in token exchange allows brute-force redemption of intercepted authorization codes","description":"@node-oauth/oauth2-server is a module for implementing an OAuth2 server in Node.js. The token exchange path accepts RFC7636-invalid code_verifier values (including one-character strings) for S256 PKCE flows. Because short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force code_verifier guesses online until token issuance succeeds.","status":"published","published":"2026-04-23T18:33:42.365Z","modified":"2026-04-25T01:23:58.715Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-307","CWE-1289"],"updated_at":"2026-11-02T13:40:57.334596Z"}],"euvd":[{"id":"EUVD-2026-25272","description":"@node-oauth/oauth2-server is a module for implementing an OAuth2 server in Node.js. The token exchange path accepts RFC7636-invalid code_verifier values (including one-character strings) for S256 PKCE flows. Because short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force code_verifier guesses online until token issuance succeeds.","status":"published","published":"2026-04-23T18:33:42Z","modified":"2026-04-25T01:23:58Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T19:24:18.224827Z"}],"ghsa":[{"id":"GHSA-jhm7-29pj-4xvf","title":"@node-oauth/oauth2-server: PKCE code_verifier ABNF not enforced in token exchange allows brute-force redemption of intercepted authorization codes","description":"## Summary\n\nThe token exchange path accepts RFC7636-invalid `code_verifier` values (including one-character strings) for `S256` PKCE flows.  \nBecause short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force `code_verifier` guesses online until token issuance succeeds.\n\n\n\n### Root cause\n\n1. `lib/pkce/pkce.js` (`getHashForCodeChallenge`) only checks that `verifier` is a non-empty string before hashing for `S256`; it does not enforce RFC7636 ABNF (`43..128` unreserved chars).\n2. `lib/grant-types/authorization-code-grant-type.js` compares `hash(code_verifier)` to stored `codeChallenge` without validating verifier format/length.\n3. In `AuthorizationCodeGrantType.handle`, authorization code revocation happens **after** verifier validation. Invalid guesses fail before revoke, so the same code can be retried repeatedly.\n\n## Steps to Reproduce\n\n### Setup\n\n- PKCE authorization code exists with:\n  - `codeChallengeMethod = \"S256\"`\n  - `codeChallenge = BASE64URL(SHA256(\"z\"))` (verifier is one character, RFC-invalid)\n- Attacker has intercepted the authorization code value.\n\n### Reproduction\n\n1. Send repeated token requests with guessed `code_verifier` values:\n\n```http\nPOST /token HTTP/1.1\nHost: oauth.example\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code\u0026\nclient_id=client1\u0026\nclient_secret=s3cret\u0026\ncode=stolen-auth-code\u0026\nredirect_uri=https://client.example/callback\u0026\ncode_verifier=\u003cguess\u003e\n```\n\n2. Observe invalid guesses return `invalid_grant`.\n3. Continue guessing (`a`..`z`).\n4. When `code_verifier=z`, token issuance succeeds and returns bearer tokens.\n\n### Confirmed PoC output\n\n```text\nBRUTE_FORCE_SUCCESS { tries: 26, guess: 'z', status: 200, tokenIssued: true }\n```\n\n## Impact\n\nAn intercepted authorization code can be redeemed by brute-forcing low-entropy verifiers that the server should have rejected under RFC7636.  \nThis weakens PKCE’s protection goal and allows token theft when clients generate short/predictable verifiers.\n\n## Recommended Fix\n\n1. Enforce `pkce.codeChallengeMatchesABNF(request.body.code_verifier)` in authorization code token exchange before hashing/comparison.\n2. Reject verifier values outside RFC7636 charset/length (`43..128` unreserved).\n3. Invalidate authorization codes on failed verifier attempts (or add strict retry limits) to prevent online guessing.","status":"published","published":"2026-04-16T21:09:50Z","modified":"2026-04-24T20:53:53Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-307","CWE-1289"],"updated_at":"2026-11-02T14:39:57.650091Z"}],"nvd":[{"id":"CVE-2026-41213","description":"@node-oauth/oauth2-server is a module for implementing an OAuth2 server in Node.js. The token exchange path accepts RFC7636-invalid code_verifier values (including one-character strings) for S256 PKCE flows. Because short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force code_verifier guesses online until token issuance succeeds.","status":"published","published":"2026-04-23T19:17:29.4Z","modified":"2026-10-26T21:10:00.17Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-307","CWE-1289"],"updated_at":"2026-11-02T14:12:15.867234Z"}],"osv":[{"id":"CVE-2026-41213","title":"@node-oauth/oauth2-server: PKCE code_verifier ABNF not enforced in token exchange allows brute-force redemption of intercepted authorization codes","description":"@node-oauth/oauth2-server is a module for implementing an OAuth2 server in Node.js. The token exchange path accepts RFC7636-invalid code_verifier values (including one-character strings) for S256 PKCE flows. Because short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force code_verifier guesses online until token issuance succeeds.","status":"published","published":"2026-04-23T18:33:42.365Z","modified":"2026-10-28T02:30:52.485791448Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-1289","CWE-307"],"updated_at":"2026-11-02T15:45:03.764807Z"},{"id":"GHSA-jhm7-29pj-4xvf","title":"@node-oauth/oauth2-server: PKCE code_verifier ABNF not enforced in token exchange allows brute-force redemption of intercepted authorization codes","description":"## Summary\n\nThe token exchange path accepts RFC7636-invalid `code_verifier` values (including one-character strings) for `S256` PKCE flows.  \nBecause short/weak verifiers are accepted and failed verifier attempts do not consume the authorization code, an attacker who intercepts an authorization code can brute-force `code_verifier` guesses online until token issuance succeeds.\n\n\n\n### Root cause\n\n1. `lib/pkce/pkce.js` (`getHashForCodeChallenge`) only checks that `verifier` is a non-empty string before hashing for `S256`; it does not enforce RFC7636 ABNF (`43..128` unreserved chars).\n2. `lib/grant-types/authorization-code-grant-type.js` compares `hash(code_verifier)` to stored `codeChallenge` without validating verifier format/length.\n3. In `AuthorizationCodeGrantType.handle`, authorization code revocation happens **after** verifier validation. Invalid guesses fail before revoke, so the same code can be retried repeatedly.\n\n## Steps to Reproduce\n\n### Setup\n\n- PKCE authorization code exists with:\n  - `codeChallengeMethod = \"S256\"`\n  - `codeChallenge = BASE64URL(SHA256(\"z\"))` (verifier is one character, RFC-invalid)\n- Attacker has intercepted the authorization code value.\n\n### Reproduction\n\n1. Send repeated token requests with guessed `code_verifier` values:\n\n```http\nPOST /token HTTP/1.1\nHost: oauth.example\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code\u0026\nclient_id=client1\u0026\nclient_secret=s3cret\u0026\ncode=stolen-auth-code\u0026\nredirect_uri=https://client.example/callback\u0026\ncode_verifier=\u003cguess\u003e\n```\n\n2. Observe invalid guesses return `invalid_grant`.\n3. Continue guessing (`a`..`z`).\n4. When `code_verifier=z`, token issuance succeeds and returns bearer tokens.\n\n### Confirmed PoC output\n\n```text\nBRUTE_FORCE_SUCCESS { tries: 26, guess: 'z', status: 200, tokenIssued: true }\n```\n\n## Impact\n\nAn intercepted authorization code can be redeemed by brute-forcing low-entropy verifiers that the server should have rejected under RFC7636.  \nThis weakens PKCE’s protection goal and allows token theft when clients generate short/predictable verifiers.\n\n## Recommended Fix\n\n1. Enforce `pkce.codeChallengeMatchesABNF(request.body.code_verifier)` in authorization code token exchange before hashing/comparison.\n2. Reject verifier values outside RFC7636 charset/length (`43..128` unreserved).\n3. Invalidate authorization codes on failed verifier attempts (or add strict retry limits) to prevent online guessing.","status":"published","published":"2026-04-16T21:09:50Z","modified":"2026-05-05T16:11:45.707781Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-1289","CWE-307"],"updated_at":"2026-11-02T15:46:50.336262Z"}]}}
