Like a hotel guest slipping a bent key into the wrong room's lock and getting the minibar inventory
CVE-2026-76169 affects Fastify versions >= 4.0.0 and < 5.12.2 — a span of over four years of releases covering both the v4 and v5 major lines. The flaw lives in how Fastify's internal router dispatches malformed request targets. Normally, each plugin prefix gets its own encapsulated setNotFoundHandler with its own authentication hooks (preValidation, preHandler). A crafted, malformed URL sent to a *public* plugin prefix can skip URL decoding and land in a *different* plugin's not-found handler — one that was supposed to be behind authentication. The result is CWE-288: authentication bypass via an alternate path. Impact is confidentiality-only (CVSS C:H/I:N/A:N): the attacker reads data the protected handler exposes, but cannot write or crash the service.
The vendor's HIGH 7.5 rating is defensible but slightly generous. The CVSS vector (AV:N/AC:L/PR:N/UI:N) correctly reflects that the attack is unauthenticated and network-reachable with low complexity. However, the real-world blast radius depends on whether the target application's not-found handler actually *serves protected data* — many Fastify apps simply return a generic {"error": "Not Found"} JSON payload from their 404 handlers, rendering the bypass moot. The advisory explicitly warns that global onRequest authentication hooks do NOT mitigate this flaw, which broadens the exploitable population beyond apps that only use prefix-scoped auth. noisgate holds at HIGH but nudges the score to 7.0 to reflect the data-exposure precondition.
4 steps from start to impact.
Identify Fastify target with plugin-prefix encapsulation
x-powered-by, error format, or response timing). They enumerate route prefixes — e.g. /public, /api, /admin — to identify which prefixes return distinct 404 responses, indicating separate setNotFoundHandler registrations per plugin.- Target runs Fastify >= 4.0.0 and < 5.12.2
- Application uses multiple plugin prefixes with separate not-found handlers
- Many Fastify deployments sit behind a reverse proxy (nginx, Cloudflare, AWS ALB) that may normalize or reject malformed URLs before they reach Fastify
- Not all apps use prefix-scoped not-found handlers; single-prefix apps have no cross-prefix to reach
Craft malformed URL targeting the wrong prefix
- Knowledge of the protected prefix name or ability to brute-force common prefixes like
/admin,/internal,/api/v2
- Upstream proxies (nginx, HAProxy, Envoy) with strict URI validation may reject or rewrite the malformed path before it reaches Node.js
- CDNs like Cloudflare normalize URLs by default, potentially neutralizing the payload
onBadUrl hook logging (if configured)Bypass authentication hooks
preValidation and preHandler authentication hooks are skipped entirely because the request was routed via the internal not-found path rather than the normal route lifecycle. The advisory confirms that even global onRequest hooks do not mitigate this bypass. The attacker's request reaches the not-found handler code with no authentication check applied.- The protected plugin's not-found handler is the target (not a regular route handler)
- If the application uses external auth (e.g., an API gateway like Kong or AWS API Gateway enforcing JWT validation upstream), the bypass at the Fastify layer is irrelevant
- Applications using middleware-based auth via
@fastify/middiemay or may not be affected depending on middleware path matching
Exfiltrate protected data from the not-found handler response
C:H per the CVSS vector, but the *actual* value depends entirely on what the handler returns.- The protected not-found handler must return data more sensitive than a generic 404 JSON stub
- Many production Fastify apps return a static
{"statusCode":404,"error":"Not Found","message":"Route not found"}with no sensitive context — in this case the bypass is a no-op - Well-configured apps strip debug output in production (
NODE_ENV=production)
The supporting signals.
| In-the-wild exploitation | None observed. Disclosed today (2026-09-04). No KEV listing, no known campaigns. |
|---|---|
| Proof-of-concept | Not yet public. No PoC repos on GitHub as of disclosure date. Finders: *vvvvvvvvvvitel* and *schecthellraiser606*; fix by *mcollina*, reviewed by *UlisesGascon*. |
| EPSS | Not yet scored — CVE just assigned. Expect initial EPSS within 7-14 days. Predicted range: low-to-moderate given AV:N/PR:N but application-specific preconditions. |
| KEV status | Not listed as of 2026-09-04. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N — 7.5 HIGH. Network-reachable, no auth needed, no user interaction. Confidentiality-only impact; no integrity or availability. |
| Affected versions | Fastify >= 4.0.0 and < 5.12.2. This covers the entire v4 major line and v5 up to 5.12.1. |
| Fixed version | 5.12.2 (npm). v4.x has no announced backport — users on v4 must upgrade to v5.12.2 or apply proxy-level mitigations. |
| Exposure footprint | Fastify has ~8-10 million weekly npm downloads. Direct internet exposure is difficult to fingerprint via Shodan/Censys since Node.js apps rarely advertise framework identity in banners unless x-powered-by is left enabled (disabled by default since Fastify v4). |
| Disclosure date | 2026-09-04 via GHSA-p68q-wchp-6fh7. |
| Reporting researchers | Found by vvvvvvvvvvitel and schecthellraiser606. Fix developed by mcollina (Fastify lead maintainer). |
noisgate verdict.
The single most decisive factor keeping this at HIGH is that the attack is unauthenticated and network-reachable with low complexity — any internet-facing Fastify app in the affected version range with prefix-encapsulated auth is a candidate. The score is nudged from 7.5 to 7.0 because real-world impact requires the not-found handler to actually serve protected data, which is an application-specific precondition that narrows the exploitable population.
Why this verdict
- Unauthenticated network access with zero interaction: The CVSS vector
AV:N/AC:L/PR:N/UI:Nmeans any scanner or script kiddie can fire this at scale. No credentials, no phishing, no user click required. This alone anchors the floor at HIGH. - Global auth hooks explicitly do not mitigate: The advisory's warning that
onRequest-level hooks are bypassed eliminates the most common compensating control developers would assume protects them. This widens the exploitable surface beyond what a casual reader might expect. - Application-specific data exposure precondition: The attack only yields value if the target not-found handler returns sensitive data. Generic 404 stubs — the default pattern in most Fastify apps — produce no exfiltrable content. This compounding friction prevents escalation to CRITICAL.
- Role multiplier: Fastify is an application-tier web framework. (a) *Low-value role* — dev/test servers: bypass is inconsequential. (b) *Typical role* — production API server: auth bypass exposes API-scoped data, blast radius is tenant/service-level, not fleet-level. (c) *High-value role* — Fastify used as an API gateway or BFF fronting microservices: auth bypass could expose aggregated backend data, but the blast radius remains bounded to that gateway's scope, not domain/fleet/supply-chain. No canonical high-value-role floor (Fastify is not a DC, hypervisor, IdP, PAM, or kernel-mode agent). Floor remains HIGH based on network-reachable auth bypass.
- No v4 backport available: The entire v4 major line (4.0.0 through end-of-life) is vulnerable with no patch. Organizations pinned to v4 must either upgrade to v5.12.2 (a major version jump) or rely on proxy-level mitigations, increasing real-world exposure duration.
Why not higher?
Escalation to CRITICAL would require fleet-scale or identity-scale blast radius. Fastify is an application framework, not infrastructure software — compromise of one Fastify service does not grant lateral movement to other hosts, domain takeover, or supply-chain pivot. The impact is confidentiality-only (I:N/A:N), and the exfiltrated data is bounded by what a single not-found handler returns. There is no code execution, no write primitive, and no persistence mechanism.
Why not lower?
Downgrading to MEDIUM would require either authenticated access or significant attack complexity. This CVE has neither — it is PR:N/AC:L, meaning unauthenticated attackers can exploit it trivially from the network. The affected version range spans four years of releases across two major versions with ~8-10M weekly downloads. Even with the data-exposure precondition, the population of exploitable apps is non-trivial.
What to do — in priority order.
- Reject malformed URLs at the reverse proxy or API gateway — Configure nginx (
merge_slashes on, strict URI validation), HAProxy, AWS ALB, or Cloudflare WAF to reject or normalize requests with malformed path segments (double-encoding, null bytes, non-standard percent sequences) before they reach Node.js. Deploy within the noisgate mitigation SLA of 30 days. This is the highest-value compensating control because it neutralizes the attack vector regardless of application code. - Upgrade to Fastify 5.12.2 — The definitive fix. Version 5.12.2 routes malformed URLs through
onBadUrl/onMaxParamLengthfor early rejection and removes the shared not-found handler pointer. For v4 users, this requires a major version upgrade — plan and test within the noisgate remediation SLA of 180 days. - Audit not-found handlers for data exposure — Review every
setNotFoundHandlercallback across your Fastify plugins. Ensure none return sensitive data (user context, internal routes, debug info, stack traces). Strip to a static 404 stub in production. This reduces impact to zero even if the routing bypass succeeds. - Move authentication to an external gateway — If auth is enforced at a dedicated API gateway (Kong, AWS API Gateway, Apigee) or identity-aware proxy (OAuth2 Proxy, Pomerium) upstream of Fastify, the Fastify-level hook bypass becomes irrelevant. This is a strategic architectural control.
- Global
onRequesthooks — The advisory explicitly states these do NOT mitigate the vulnerability. Do not rely on application-level global auth hooks as a compensating control. - Rate limiting alone — Rate limiting slows the attacker but does not prevent the auth bypass. A single well-crafted request is sufficient to exfiltrate data from the handler response.
- Disabling
x-powered-by— While good hygiene, this only removes one fingerprinting vector. Attackers can identify Fastify through error response formats, timing, or other behavioral signals.
Crowdsourced verification payload.
Run this script on any host where a Fastify application is deployed, or from a CI pipeline that has access to the application's node_modules. No special privileges required — just read access to the package.json or node_modules/fastify/package.json. Example: bash check_cve_2026_76169.sh /opt/myapp
#!/usr/bin/env bash\n# check_cve_2026_76169.sh — Detect CVE-2026-76169 (Fastify auth bypass via malformed URL routing)\n# Usage: bash check_cve_2026_76169.sh <path-to-app-root>\n# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN\n\nset -euo pipefail\n\nAPP_DIR=\"${1:-.}\"\nPKG_JSON=\"${APP_DIR}/node_modules/fastify/package.json\"\n\nif [ ! -f \"$PKG_JSON\" ]; then\n echo \"UNKNOWN — fastify not found in ${APP_DIR}/node_modules\"\n exit 2\nfi\n\nVERSION=$(grep -o '\"version\": *\"[^\"]*\"' \"$PKG_JSON\" | head -1 | sed 's/.*\"\\([^\"]*\\)\"/\\1/')\n\nif [ -z \"$VERSION\" ]; then\n echo \"UNKNOWN — could not parse fastify version from $PKG_JSON\"\n exit 2\nfi\n\necho \"Detected fastify version: $VERSION\"\n\n# Parse semver components\nMAJOR=$(echo \"$VERSION\" | cut -d. -f1)\nMINOR=$(echo \"$VERSION\" | cut -d. -f2)\nPATCH=$(echo \"$VERSION\" | cut -d. -f3 | cut -d- -f1)\n\n# Vulnerable: >= 4.0.0 and < 5.12.2\nif [ \"$MAJOR\" -lt 4 ]; then\n echo \"PATCHED — version $VERSION is below the affected range (< 4.0.0)\"\n exit 0\nelif [ \"$MAJOR\" -eq 4 ]; then\n echo \"VULNERABLE — version $VERSION is in the affected v4 range (no backport available)\"\n exit 1\nelif [ \"$MAJOR\" -eq 5 ]; then\n if [ \"$MINOR\" -lt 12 ]; then\n echo \"VULNERABLE — version $VERSION is below the fix (5.12.2)\"\n exit 1\n elif [ \"$MINOR\" -eq 12 ] && [ \"$PATCH\" -lt 2 ]; then\n echo \"VULNERABLE — version $VERSION is below the fix (5.12.2)\"\n exit 1\n else\n echo \"PATCHED — version $VERSION includes the fix\"\n exit 0\n fi\nelif [ \"$MAJOR\" -gt 5 ]; then\n echo \"PATCHED — version $VERSION is above the affected range\"\n exit 0\nfi\n\necho \"UNKNOWN — could not determine vulnerability status\"\nexit 2If you remember one thing.
npm ls fastify or your SBOM tooling and flag anything >= 4.0.0 and < 5.12.2. Per the noisgate mitigation SLA for HIGH, deploy compensating controls — specifically, malformed-URL rejection at your reverse proxy or API gateway — within 30 days. Simultaneously audit every setNotFoundHandler callback for data exposure and strip any sensitive content. Per the noisgate remediation SLA for HIGH, complete the upgrade to Fastify 5.12.2 within 180 days. For teams still on the v4 major line (no backport available), treat the major version upgrade as the remediation target and lean harder on the proxy-level mitigation in the interim. Monitor for PoC releases and EPSS movement over the next 2-4 weeks — if exploitation emerges, compress the mitigation timeline to hours.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.