CVE-2026-18165
Executive Summary
CVE-2026-18165 is a login-CSRF vulnerability (CWE-352) in @fastify/oauth2, the official OAuth 2.0 plugin for the Fastify web framework, affecting versions from 7.2.0 up to but not including 8.3.0. The plugin validates the OAuth state parameter — and, when PKCE is enabled, the code_verifier — by comparing the callback query parameter against an unprefixed, predictable cookie, with no server-side binding to the browser that initiated the flow. An attacker who can write a cookie for the application's host, such as a sibling subdomain under the same registrable domain, can plant matching state and verifier cookies and complete an attacker-owned OAuth flow inside a victim's browser, silently signing the victim into the attacker's account. The victim's own account, credentials, and tokens are never exposed; the issue is fixed in @fastify/oauth2 8.3.0 via the opt-in hostPrefixedCookies option. Base CVSS score: 4.2.
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 OAuth 2.0 Authorization Code flow relies on the state parameter as the canonical defense against CSRF on the redirect/callback endpoint. RFC 6749 Section 10.12 requires that the client bind the state value to the user-agent session that initiated the flow so that the callback cannot be replayed or forged cross-session. When PKCE (RFC 7636) is used, the code_verifier serves an analogous binding role: it proves the party exchanging the authorization code is the same party that started the flow. If either binding is broken by storing the value client-side in a predictable, attacker-writable cookie, the flow becomes a login-CSRF primitive.
In @fastify/oauth2 versions 7.2.0 through 8.3.0 (exclusive), the plugin's implementation stores the OAuth state value — and the PKCE code_verifier when present — in an HTTP cookie that is:
- Unprefixed — it does not use a
__Host-or__Secure-cookie name prefix. A__Host-prefix would enforce host-only scope, Secure flag, and noDomainattribute, making the cookie unreadable/unwritable by sibling subdomains. Absent the prefix, the cookie may be scoped with aDomainattribute covering the registrable domain (for example,.example.com), meaning it is also readable and writable by any sibling subdomain. - Predictable — the description identifies the cookie values as predictable, meaning an attacker can obtain or derive a valid
state/code_verifiervalue and pre-plant it in the victim's browser before the legitimate flow begins. - Lacking server-side binding — the value exists purely client-side; nothing ties it to the actual browser session, IP, TLS session, or server-side session store that initiated the OAuth flow.
Validation logic affected: on the callback request, the plugin compares the state query parameter against the cookie value, and when PKCE is active it uses the cookie's code_verifier for the subsequent token exchange. Because the attacker controls both cookie values, they can complete a full OAuth exchange for an account they own while the victim's browser performs the HTTP requests. The result is classic login CSRF: the victim's browser ends up holding an authenticated session for the attacker's account, not the victim's account.
Attack surface: any Fastify service exposing an OAuth login path built on @fastify/oauth2, deployed on a host whose parent registrable domain also contains attacker-controlled sibling subdomains. The cookie-write primitive can be achieved via a sibling subdomain that can set Domain-scoped cookies, or via any other cookie-injection weakness (e.g., XSS on a sibling host, captive portal injection, or cleartext HTTP on a sibling host if cookies lack the Secure flag). This is a client-side, user-interaction-driven attack chain; no server-side authentication bypass exists, and no victim credentials or tokens are exfiltrated.
Fix behavior (8.3.0): the new opt-in hostPrefixedCookies option switches the state/verifier cookies to a host-prefixed scheme, which constrains the cookie to the exact host and HTTPS, blocking sibling-subdomain planting. The NVD description additionally recommends binding state to a server-side session as an alternative mitigation.
Exploitation Scenarios
No public PoC is available for CVE-2026-18165, and no exploitation in the wild is reported in the provided intelligence data. The attack chain below is inferred from the vulnerability class (CWE-352 login CSRF) and the NVD description.
Step 1 — Reconnaissance / target identification. The attacker locates a Fastify web application using @fastify/oauth2 (versions 7.2.0 ≤ v < 8.3.0). Fingerprinting may be based on the application's login page redirecting to a well-known OAuth authorization endpoint, observable Fastify headers, or client-side artifacts. The attacker also enumerates the application's registrable domain to confirm the existence of a sibling subdomain they control or can influence (e.g., user-content.example.com under example.com where the target is app.example.com).
Step 2 — Establish attacker-owned OAuth parameters. The attacker registers their own OAuth client/account at the same identity provider used by the target application, then initiates an authorization code flow under their own account. They capture the state value used in their flow and, if PKCE is employed, the corresponding code_verifier value. The attacker then crafts a callback URL to the target application containing their own authorization code and the matching state query parameter.
Step 3 — Cookie planting and victim delivery. From the sibling subdomain, the attacker plants two cookies scoped to the application's host (via Domain covering the registrable domain): one named for the OAuth state and one for the code_verifier, both containing the attacker's values. The attacker then lures the victim into opening the crafted callback URL — via phishing, a link on a forum, or any HTTP redirect the victim trusts. Because the plugin validates the callback state query parameter against the (attacker-planted) cookie and finds a match, the flow proceeds normally.
Step 4 — Post-exploitation. Inside the victim's browser, the plugin completes the token exchange using the attacker's authorization code and the planted code_verifier. The application issues the victim's browser a session cookie bound to the attacker's account. From the victim's perspective, the application silently signed them in as the attacker — login CSRF. The victim may subsequently perform actions, submit data, or view content within the attacker's account context, contaminating the attacker's account with the victim's activity and creating false audit trails. The victim's own account remains untouched, and no victim credentials or tokens are exposed.
Impact Assessment
Affected systems: Fastify applications using @fastify/oauth2 versions 7.2.0 through 8.2.x, with OAuth login exposed to end users and deployed on hosts sharing a registrable domain with any less-trusted sibling subdomain. Enterprises running multiple subdomains under one eTLD+1, or accepting cookies from third-party subdomains, are disproportionately exposed.
Business impact: The primary impact is identity confusion rather than data theft. A successful attack silently authenticates victims into the attacker's account, enabling: (a) account-entitlement confusion where victims are granted access rights belonging to the attacker; (b) contamination of the attacker's account with victim activity, producing poisoned logs and false audit records; (c) reputational and compliance damage for the service provider, since users may attribute attacker-account content to themselves; (d) follow-on social engineering, since the attacker can observe the victim's behavior inside the attacker-owned account (low confidentiality impact).
CVSS context (4.2, vector CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N): The network attack vector and zero-privilege requirement are mitigated by high attack complexity (a cookie-write primitive is required, e.g., sibling-subdomain control) and mandatory user interaction. Scope is unchanged; confidentiality and integrity impacts are rated Low because the victim's own data is never exposed and no data tampering occurs — the attacker only redirects the victim into an attacker-owned session. Availability is not affected. EPSS has not yet scored this CVE; it is not listed on the CISA KEV catalog.
Detection Guidance
The following detections target observable artifacts of the attack: anomalous OAuth callback requests, spikes in state-validation mismatches, suspicious Set-Cookie responses carrying state/verifier values from non-OAuth endpoints, and OAuth flows completed with unexpected referrers or user-agents. No public PoC exists, so these rules are heuristic and should be tuned to the local environment.
Sigma Rule
title: Fastify OAuth2 Login CSRF - State Mismatch Burst Followed by Callback Success
id: f5a50f3c-9a6e-4c1b-8d3a-7e92b0a1c4d1
status: experimental
description: >
Detects a burst of OAuth state-validation failures followed by a successful
callback within a short window, consistent with cookie planting for
CVE-2026-18165 (login CSRF in @fastify/oauth2 < 8.3.0).
logsource:
category: application
product: fastify
service: oauth
detection:
selection_errors:
event: oauth_callback
outcome|contains:
- "state mismatch"
- "invalid state"
- "code_verifier mismatch"
selection_success:
event: oauth_callback
outcome: success
timeframe: 10m
condition: selection_errors | count() > 5 by src_ip and selection_success by src_ip
level: medium
tags:
- attack.t1189
- cwe.352
- cve.2026.18165
YARA Rule
rule fastify_oauth2_login_csrf_cookie_planting {
meta:
author = "ValtersIT Threat Intelligence"
description = "Detect scripts or payloads that plant state/code_verifier cookies targeting @fastify/oauth2 (CVE-2026-18165)"
cve = "CVE-2026-18165"
cwe = "CWE-352"
date = "2026-01-01"
strings:
$s1 = "document.cookie" ascii wide
$s2 = "state=" ascii wide
$s3 = "code_verifier=" ascii wide
$s4 = "domain=" ascii wide
$s5 = "expires=" ascii wide
$s6 = "@fastify/oauth2" ascii wide
$s7 = "hostPrefixedCookies" ascii wide
condition:
uint16(0) == 0x5A4D or $s1
and 2 of ($s2, $s3)
and $s4
and any of ($s6, $s7)
}
Suricata IDS Rule
alert http any any -> $HOME_NET any ( \
msg:"CVE-2026-18165 Fastify OAuth2 state/code_verifier cookie planting attempt"; \
flow:established,to_client; \
http.response; \
http.set_cookie; content:"state="; nocase; \
http.set_cookie; content:"code_verifier="; nocase; \
not http.uri; content:"oauth"; nocase; \
sid:202618165; rev:1; \
metadata:cve CVE-2026-18165;)
This rule alerts when any non-OAuth HTTP response from a monitored web server sets both a state= and a code_verifier= cookie — an abnormal pattern indicating an endpoint on a sibling subdomain is planting verifier/state values for the OAuth host. Adjust $HOME_NET to the application host range; disable not http.uri if the application legitimately sets both cookies on login-preview pages.
Elastic Detection
sequence by client.ip, user.name with maxspan=15m
[ network where event.category == "web"
and url.path like "*oauth*"
and url.query like "*state=*"
and http.response.status_code == 302 ]
[ network where event.category == "web"
and url.path like "*callback*"
and url.query like "*code=*"
and url.query like "*state=*"
and http.response.status_code == 200 ]
This EQL sequence tracks a rapid OAuth initiation (302 redirect) followed by a successful callback containing both code and state — a complete authorization-code flow. Repeated occurrences from the same client.ip for a single user.name, or flows where the initiating step originates from an unusual referrer, warrant investigation as login-CSRF candidates.
Splunk SPL Query
index=web sourcetype=access_combined
| rex field=uri ".*oauth/callback\?.*state=(?<state_val>[^&\"]+)"
| where isnotnull(state_val)
| eval referer_domain=lower(trim(coalesce(referer_domain,"unknown")))
| stats count BY _time, src_ip, user, referer_domain, state_val
| sort 0 - _time
| where count > 3
Supplement with a state-mismatch burst detector against application logs:
index=app sourcetype=nodejs_logs "state" "mismatch"
| bucket _time span=5m
| stats count AS state_errors BY _time, host, src_ip
| where state_errors > 5
| join type=inner _time, src_ip [
search index=app sourcetype=nodejs_logs "callback" "success"
| stats count AS callback_success BY _time, src_ip ]
Wazuh OSSEC Rule
<group name="fastify_oauth2,">
<rule id="100826" level="12" frequency="6" timeframe="300">
<if_matched_sid>100825</if_matched_sid>
<match>state mismatch|invalid state|code_verifier mismatch</match>
<description>CVE-2026-18165: Repeated OAuth state validation failures - possible login CSRF cookie planting</description>
<group>authentication_failures,web_application_attack,</group>
<mitre>
<id>T1189</id>
</mitre>
</rule>
</group>
The parent rule (ID 100825, shown below) should extract OAuth callback log lines for local tuning:
<group name="fastify_oauth2,">
<rule id="100825" level="5">
<decoded>oauth_callback</decoded>
<description>OAuth callback processed by @fastify/oauth2</description>
<group>web_application,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence is 0/4 (UNPATCHED) in tracked sources. The NVD description states that @fastify/oauth2 8.3.0 fixes the issue via the opt-in hostPrefixedCookies option, but no official vendor advisory, patch diff, or release commit has been confirmed by any tracked patch source in this feed. Treat the fix as unverified until independently confirmed against the upstream repository's 8.3.0 release.
Immediate mitigations (no patch applied yet):
- Bind OAuth
state(and, where applicable, the PKCEcode_verifier) to a server-side session store instead of a client-side cookie. Verify the values against session data at the callback and reject mismatches. - If the application cannot be modified, disable PKCE and require the identity provider to reject flows lacking
state; additionally add strict Referer/Host validation on the callback endpoint. - Harden cookie policy: ensure all application cookies use
Secure; SameSite=Lax/Strict; HttpOnlywhere applicable, and eliminate anyDomainattribute on cookies scoped to the registrable domain. - Review cookie-write exposure: remediate XSS on sibling subdomains, enforce HTTPS/HSTS across the entire registrable domain, and audit which subdomains can set
Domain-scoped cookies.
- Bind OAuth
Patch / upgrade steps:
- Upgrade to @fastify/oauth2 8.3.0 or later.
- After upgrade, enable the new
hostPrefixedCookiesoption in the plugin configuration (per the NVD description). This applies__Host--style cookie semantics so state/verifier cookies are host-only, Secure, and cannot be planted by sibling subdomains. - Regression-test the full OAuth login flow (including PKCE and refresh flows) after enabling the option, since cookie-name changes may invalidate in-progress sessions.
Long-term hardening:
- Enforce cookie-prefix standards (
__Host-/__Secure-) as a default for all first-party cookies, not just OAuth state. - Adopt the NVD-recommended practice of server-side session binding for all OAuth state parameters (RFC 6749 §10.12; BCP 195).
- Maintain an inventory of all subdomains under the registrable domain; eliminate subdomain sprawl, rotate credentials for subdomains, and monitor for subdomain takeover, which can escalate to the cookie-write primitive this CVE requires.
- Deploy the detection rules above on the application's web and application logs, and alert on state-mismatch bursts and abnormal callback completions.
- Verify future releases against the official @fastify/oauth2 repository before deployment; the current tracked patch confidence of 0/4 indicates this CVE may be updated as public exploit research develops.
- Enforce cookie-prefix standards (
References
- NVD Entry — CVE-2026-18165: https://nvd.nist.gov/vuln/detail/CVE-2026-18165
- MITRE CWE-352 — Cross-Site Request Forgery: https://cwe.mitre.org/data/definitions/352.html
- OWASP — Cross-Site Request Forgery Prevention Cheat Sheet (login-CSRF countermeasures): https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- RFC 6749 §10.12 — OAuth 2.0 CSRF protection via state parameter: https://datatracker.ietf.org/doc/html/rfc6749#section-10.12
- RFC 7636 — Proof Key for Code Exchange (PKCE): https://datatracker.ietf.org/doc/html/rfc7636
No CISA KEV listing, no known exploit-in-the-wild activity, and no vendor advisory URL were provided in the intelligence data for this CVE. No threat-actor campaigns are currently associated with CVE-2026-18165.
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.