CVE-2026-18500
Executive Summary
CVE-2026-18500 is an authentication-bypass vulnerability in the @fastify/jwt plugin for Fastify, affecting all versions before 10.2.2. When an application passes a per-request verification key via request.jwtVerify({ key }), the plugin's option-merge logic silently overrides that key with the globally configured secret, causing tokens to be verified against the wrong trust domain. An authenticated user holding a token signed with the global secret can therefore access routes that explicitly require a different key — for example, admin-only endpoints — without knowing either secret. The flaw is fixed in @fastify/jwt 10.2.2, where an explicit per-call key takes precedence over the global secret.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The vulnerability is classified under CWE-347 (Improper Verification of Cryptographic Signature). The affected component is @fastify/jwt, a JSON Web Token (JWT) authentication plugin for the Fastify web framework. The defect resides in the option-merge routine used when the application invokes the per-request verification API, request.jwtVerify({ key }).
According to the NVD description, the intent of this API is to allow a caller to supply a verification key that is specific to a single request or route — enabling applications to maintain multiple key-based trust domains (for example, a user key and an admin key). The bug is that the option merge applies the globally configured secret last, so the caller-supplied key is silently discarded and replaced by the plugin's global secret. The verification then occurs against the wrong key.
From a cryptographic-signature standpoint, the mechanism is not a key-recovery or brute-force issue. The signature algorithm executes normally; the flaw is purely in which key is used for verification. The token is validated against the global secret rather than the per-route key, so a token that would normally fail validation against the route's intended key is accepted. The trust boundary enforced by distinct signing keys is effectively collapsed: every token signed with the global secret becomes valid on every route, regardless of the key that route was configured to require.
The attack surface is any Fastify application that:
- Uses @fastify/jwt versions before 10.2.2;
- Configures a global JWT secret at plugin registration; and
- Relies on per-request
keyoverrides to separate authorization domains (user vs. admin, tenant A vs. tenant B, internal vs. partner).
The vulnerability is server-side configuration/state logic within the plugin itself — there is no injected payload and no network-level exploit syntax. Because the global secret is applied last during option merge, the developer's explicit per-call intent is inverted. The fix in 10.2.2 reverses this precedence so that an explicit per-call key takes priority over the global secret. Insufficient technical detail is available on the exact merge implementation (e.g., whether a shallow or deep clone is used), as the NVD description does not disclose internal helper function names or source file paths.
Exploitation Scenarios
No public PoC or exploit code was observed in the provided data, and no Nuclei template exists. The attack chain below is inferred from the vulnerability class and the NVD description; it should be treated as a plausible exploitation path rather than a confirmed in-the-wild campaign.
Reconnaissance / target identification. An attacker locates a public-facing Fastify application that uses @fastify/jwt. Fingerprinting may reveal the plugin's presence through HTTP response headers, typical Fastify 404/500 responses, or public JavaScript bundle/config exposure. The attacker maps the application's route structure to identify which endpoints rely on global-key tokens (e.g., ordinary user APIs) vs. endpoints expected to require a different verification key (e.g.,
/admin,/internal, partner APIs). Such routes are commonly documented or inferable from client-side code.Triggering the flaw. The attacker obtains a legitimate token signed with the global secret by registering or logging in as an ordinary user — no secret knowledge is required to obtain this token. The attacker then replays that token on a route protected by a different key, e.g., by placing it in the standard
Authorization: Bearer <token>header of a request to the admin route. On the server, the route handler callsrequest.jwtVerify({ key })with the admin-specific key, but the bug causes the plugin's option merge to apply the global secret last, silently overriding the per-call key.Payload / execution method. There is no injection payload. The JWT itself is the execution artifact: because verification is performed against the global secret, the attacker's ordinary user token passes signature verification even though the route demanded a different key. The JWT's claims remain unchanged; the server simply validates them with the wrong trust anchor.
Post-exploitation impact. The attacker crosses the key-based trust boundary and gains access to the authorization domain associated with the overridden route — e.g., administrative functions or privileged data retrieval. The NVD description emphasizes that separation between "user and admin keys" is specifically broken. The attacker can then read sensitive data (confidentiality), perform privileged modifications (integrity), and pivot to further administrative actions. No knowledge of either secret is required, which makes the escalation reliably executable by any low-privileged authenticated user.
Impact Assessment
The CVSS v3.1 base score is 8.1 (High) with vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N. Notable subscore implications:
- Attack Vector: Network (AV:N) — the flaw is exploitable remotely over HTTP.
- Attack Complexity: Low (AC:L) — no special conditions or race windows required; the attacker simply sends a token.
- Privileges Required: Low (PR:L) — the attacker must be an authenticated user with a valid global-secret token, meaning ordinary user credentials suffice.
- User Interaction: None (UI:N) — no victim action is needed.
- Scope: Unchanged (S:U) — the compromise does not cross into a separate security authority boundary at the platform level; the trust boundary that is crossed is application-internal (key-based domains).
- Confidentiality / Integrity Impact: High (C:H/I:H) — privileged data exposure and unauthorized modification of resources in the target domain.
- Availability Impact: None (A:N) — the flaw does not directly enable denial of service.
Business impact. Applications that partition authorization domains by signing key are common in multi-tenant SaaS, B2B integrations, and platforms that separate standard users from administrators or partners. This vulnerability converts any valid low-privileged account into a privileged one within the affected domains, potentially enabling account takeover of admin sessions, sensitive data exfiltration, and unauthorized state changes. The blast radius is limited to deployments matching the vulnerable usage pattern (per-call keys in versions < 10.2.2), but such deployments are precisely the ones that believe they have a strong key-based trust boundary.
Affected systems. Fastify applications using @fastify/jwt prior to 10.2.2 with per-request key overrides and a globally configured secret.
Exploit status. No public PoC, exploit code, or evidence of in-the-wild exploitation was provided. The CVE is not listed in CISA KEV. EPSS has not yet been scored by FIRST.org, so no exploit-probability estimate should be fabricated.
Detection Guidance
Detection of this flaw at the network layer is inherently difficult: the JWT, the HTTP method, and the URI are all legitimate, and the failure is in the server's key-selection logic. The rules below therefore focus on two observable signals: (1) anomalous successful access to privileged routes using tokens that were issued for a different trust domain, and (2) presence of the known-vulnerable dependency version in application manifests. Field names in the detection rules follow conventional web-server and application-log schemas (ECS for Elastic, Common Event Format for Splunk, Apache/nginx access-log conventions for Sigma/Wazuh) and should be adapted to local logging standards.
Sigma Rule
title: CVE-2026-18500 - Fastify JWT Cross-Key Admin Route Access
id: 5a3f8c10-9c42-4b1e-8f6a-2d7b0e4c9a11
status: experimental
description: >
Detects successful HTTP requests to administrative or privileged routes that
present a JWT bearer token. In Fastify applications using @fastify/jwt < 10.2.2,
such a token may have been verified against the global secret rather than the
per-route key, indicating a cross-key trust-boundary bypass (CVE-2026-18500).
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-18500
author: Threat Intelligence - valtersit.com
date: 2026/05/14
logsource:
category: web_server
product: generic
definition: Requires access logs containing HTTP Authorization header content and response status.
detection:
selection_bearer:
http.authorization|contains: "Bearer "
selection_jwt:
http.authorization|startswith: "Bearer eyJ"
selection_privileged_paths:
http.url|contains:
- "/admin"
- "/internal"
- "/api/admin"
- "/graphql"
selection_success:
http.response.status_code:
- 200
- 201
- 204
filter_static_assets:
http.url|endswith:
- ".css"
- ".js"
- ".png"
- ".ico"
- ".svg"
condition: selection_bearer and selection_jwt and selection_privileged_paths and selection_success and not filter_static_assets
falsepositives:
- Legitimate privileged users accessing admin routes with their own tokens.
- Tokens that happen to be valid for the privileged domain.
level: medium
YARA Rule
This rule targets application source and lock-file artifacts (package-lock.json, pnpm-lock.yaml, yarn.lock, package.json) to identify deployments carrying the vulnerable @fastify/jwt version range before 10.2.2.
rule fastify_jwt_cve_2026_18500_vulnerable_version
{
meta:
author = "Threat Intelligence - valtersit.com"
description = "Detects @fastify/jwt dependency declarations in the vulnerable version range (< 10.2.2)"
cve = "CVE-2026-18500"
cwe = "CWE-347"
date = "2026-05-14"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-18500"
strings:
$pkg = "@fastify/jwt"
$v10 = /"(@fastify\/jwt)"\s*[:=]\s*"[~^]?10\.0\.[0-9]+"/ nocase
$v101 = /"(@fastify\/jwt)"\s*[:=]\s*"[~^]?10\.1\.[0-9]+"/ nocase
$lock = /"node_modules\/@fastify\/jwt"\s*:\s*\{[^}]*"version"\s*:\s*"10\.[01]\.[0-9]+"/
condition:
$pkg and any of ($v10, $v101, $lock)
}
Suricata IDS Rule
Network-layer rule that alerts when a client presents a JWT (Bearer token beginning with eyJ) to a privileged or administrative URI path. This is an anomaly indicator: privileged routes in affected applications should only be reached with tokens of the correct trust domain, and any failure to correlate should be surfaced for investigation.
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"CVE-2026-18500 - Potential Fastify JWT cross-key access to privileged route"; flow:established,to_server; content:"/admin"; http.uri; content:"Bearer eyJ"; http.authorization; content:"/api/admin"; http.uri; content:"/internal"; http.uri; classtype:attempted-admin; sid:202618500; rev:1; metadata:created_at 2026_05_14, updated_at 2026_05_14;)
Note: because the tampering occurs during server-side key selection, this signature can only flag the combination of a JWT token and a privileged route; it cannot by itself distinguish a valid privileged token from an invalid one. Response-code correlation in a SIEM is recommended.
Elastic Detection
EQL correlation that chains a privileged-route request bearing a JWT bearer token with a subsequent successful response from the same source IP, over a short time window. This approximates a "low-privilege token accepted on a high-privilege route" event without requiring application-internal logs.
sequence by source.ip with maxspan=30s
[ network where http.request.method in ("GET", "POST", "PUT", "PATCH", "DELETE")
and startsWith(http.request.headers.authorization, "Bearer eyJ")
and (contains(http.url.path, "/admin") or contains(http.url.path, "/internal")
or contains(http.url.path, "/api/admin")) ]
[ network where http.response.status_code in (200, 201, 204) ]
If application logs exposing request.jwtVerify({ key }) behavior are available, SOC teams should additionally profile the rate of 401/403 → 200 transitions on per-key routes, since the bug produces exactly this signature change for previously rejected tokens.
Splunk SPL Query
index=web sourcetype=access_combined
| rex field=uri_path "(?<path>/[^/]+)"
| where match(auth_header, "^Bearer eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$")
| regex uri_path="(?i)(admin|internal|api/admin|privileged)"
| stats count min(_time) as first_seen max(_time) as last_seen
by src_ip, uri_path, auth_claims_user, status
| where status IN (200, 201, 204)
| eval suspicious = if(match(auth_claims_user, "user|member|customer"), "HIGH", "review")
| sort - count
| table first_seen, last_seen, src_ip, uri_path, auth_claims_user, status, suspicious, count
Analysts should pivot on suspicious=HIGH results: a JWT whose claims identify the subject as a normal user, accepted by an admin route with an HTTP 200, is the canonical fingerprint of this vulnerability being exercised.
Wazuh OSSEC Rule
The following local rule triggers on web-server logs that combine a JWT bearer token with a privileged URI and a successful status code. It should be placed in /var/ossec/etc/rules/local_rules.xml.
<group name="fastify,jwt,cve-2026-18500,">
<rule id="100185" level="12">
<decoded_as>web-access</decoded_as>
<match>Bearer eyJ</match>
<regex>/(admin|internal|api/admin|privileged)</regex>
<regex>\s(200|201|204)\s</regex>
<description>CVE-2026-18500 (possible): JWT bearer token accepted on privileged route - cross-key trust boundary bypass may have occurred in @fastify/jwt < 10.2.2</description>
<group>authentication,authorization_bypass,attack,pci_dss_6.5.8,</group>
<mitre>
<id>T1190</id>
</mitre>
</rule>
</group>
Matches should be correlated with the source IP's prior authentication history; a first-time successful 200 on an admin route from a previously low-privilege account is a strong incident candidate.
Mitigation & Patch Guidance
Patch confidence: 0/4 (UNPATCHED — no patch evidence from any tracked source). No patch has been ingested by the tracked patch sources, and the patch-confidence field is 0/4. Note, however, that the NVD description explicitly identifies the upstream fix: "The issue is fixed in @fastify/jwt 10.2.2, where an explicit per-call key takes precedence over the global secret. Users should upgrade to 10.2.2." Until the fix is verified in your environment's dependency tree, treat the vulnerability as exploitable.
Immediate mitigations.
- Identify all deployments of @fastify/jwt with versions
< 10.2.2(use the YARA rule against repositories and lock files; auditpackage-lock.json/pnpm-lock.yaml). - Where an immediate upgrade is not possible, avoid relying on per-request
keyoverrides as the sole trust boundary. Use separate plugin instances with distinct global secrets for each authorization domain so that the option-merge flaw cannot collapse two domains. - If both global and per-call keys must coexist, add an explicit application-level check that compares the token's signer or key identifier metadata against the route's expected key before processing the request — do not rely on the plugin's merge order.
- Treat the vulnerability as an authentication-bypass incident trigger: immediately review access logs for successful (200/201/204) requests to admin/internal routes by accounts whose prior activity is exclusively low-privileged.
- Identify all deployments of @fastify/jwt with versions
Patch / upgrade steps.
- Upgrade to @fastify/jwt 10.2.2 in all affected applications:
npm install @fastify/jwt@10.2.2(or the equivalent for the package manager in use). - Re-run the dependency-scope YARA rule and lock-file audit to confirm that no transitive or nested dependency still resolves to a version
< 10.2.2. - After deployment, execute regression tests that specifically assert an explicitly provided per-call
keyis honored over the global secret onrequest.jwtVerify({ key }).
- Upgrade to @fastify/jwt 10.2.2 in all affected applications:
Long-term hardening.
- Add automated CI checks that fail builds on vulnerable @fastify/jwt versions (e.g.,
npm audit, SCA tooling) and on dependency manifests matching the YARA signatures above. - Perform a security review of any code path where key material or verification options are merged; prefer explicit, non-overridable configuration for trust anchors.
- Incorporate anomaly detection (the Sigma/Elastic/Splunk rules above) into the SIEM so that successful privileged-route access by unexpected accounts triggers alerting.
- Monitor CISA KEV and FIRST.org EPSS for future scoring; if the CVE is added to KEV or a public PoC emerges, escalate to active-hunting and consider forced re-authentication of privileged sessions.
- Add automated CI checks that fail builds on vulnerable @fastify/jwt versions (e.g.,
References
- NVD Entry — CVE-2026-18500: https://nvd.nist.gov/vuln/detail/CVE-2026-18500
- MITRE CWE-347 (Improper Verification of Cryptographic Signature): https://cwe.mitre.org/data/definitions/347.html
- MITRE ATT&CK Technique T1190 — Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/
- CISA Known Exploited Vulnerabilities (KEV) Catalog — CVE-2026-18500 is not listed as of this report; no exploit-in-the-wild evidence was provided.
- Vendor fix reference (per NVD description): upgrade to @fastify/jwt 10.2.2. No vendor advisory URL, researcher analysis, or third-party blog was supplied in the CVE data; no threat-actor campaigns are known at the time of writing, and no public PoC or Nuclei template exists.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.