Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources or documents need attention: 274/275 feeds/APIs · 469/469 docs · 9/9 advisories · checked 08:00 UTC Customize Public modeDiscuss ChangeIntel on Discord

h3 before 2.0.1-rc.18 Denial of Service via Unbounded Chunked Cookie

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-86250

Published

h3 versions before 2.0.1-rc.18 fail to validate the chunk count parsed from user-controlled cookie values in setChunkedCookie() and deleteChunkedCookie() functions. Attackers can send a crafted cookie header with an extremely large chunk count to trigger an O(n²) cleanup loop that hangs the server process.

Source published
6 Sep 2026 12:00:26 UTC
Source modified
18 Sep 2026 17:23:10 UTC
Observed here
23 Oct 2026 15:48:27 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 4.0 · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

CVSS 3.1 · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

euvd · EUVD-2026-72106

Published

h3 versions before 2.0.1-rc.18 fail to validate the chunk count parsed from user-controlled cookie values in setChunkedCookie() and deleteChunkedCookie() functions. Attackers can send a crafted cookie header with an extremely large chunk count to trigger an O(n²) cleanup loop that hangs the server process.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Sep 6, 2026, 12:00:26 PM

dateUpdated: Sep 18, 2026, 5:23:10 PM

Source published
Sep 6, 2026, 12:00:26 PM (timezone not stated)
Source modified
Sep 18, 2026, 5:23:10 PM (timezone not stated)
Observed here
11 Nov 2026 17:02:31 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 4.0 · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

nvd · CVE-2026-86250

Published

h3 versions before 2.0.1-rc.18 fail to validate the chunk count parsed from user-controlled cookie values in setChunkedCookie() and deleteChunkedCookie() functions. Attackers can send a crafted cookie header with an extremely large chunk count to trigger an O(n²) cleanup loop that hangs the server process.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Awaiting Analysis

Weakness classifications: CWE-400

Original publisher date fields

published: 2026-09-06T12:17:15.767

lastModified: 2026-09-18T18:17:18.657

Source published
6 Sep 2026 12:17:15 UTC
Source modified
18 Sep 2026 18:17:18 UTC
Observed here
25 Oct 2026 16:19:16 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 4.0 · [email protected] · Secondary · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

CVSS 3.1 · [email protected] · Secondary · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

osv · GHSA-fg72-q65g-wm5w

Withdrawn

## Duplicate Advisory This advisory has been withdrawn because it is a duplicate of GHSA-q5pr-72pq-83v3. This link is maintained to preserve external references. ## Original Description h3 versions before 2.0.1-rc.18 fail to validate the chunk count parsed from user-controlled cookie values in setChunkedCookie() and deleteChunkedCookie() functions. Attackers can send a crafted cookie header with an extremely large chunk count to trigger an O(n²) cleanup loop that hangs the server process.

Source published
6 Sep 2026 12:30:23 UTC
Source modified
1 Oct 2026 00:00:04 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CVSS_V4 · CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Publisher rating · HIGH

osv · GHSA-q5pr-72pq-83v3

Published

## Summary The `setChunkedCookie()` and `deleteChunkedCookie()` functions in h3 trust the chunk count parsed from a user-controlled cookie value (`__chunked__N`) without any upper bound validation. An unauthenticated attacker can send a single request with a crafted cookie header (e.g., `Cookie: h3=__chunked__999999`) to any endpoint using sessions, causing the server to enter an O(n²) loop that hangs the process. ## Details The chunked cookie system stores large cookie values by splitting them into numbered chunks. The main cookie stores a sentinel value `__chunked__N` indicating how many chunks exist. When setting a new chunked cookie, the code cleans up any previous chunks that are no longer needed. The vulnerability is in `getChunkedCookieCount()` at `src/utils/cookie.ts:244-249`: ```typescript function getChunkedCookieCount(cookie: string | undefined): number { if (!cookie?.startsWith(CHUNKED_COOKIE)) { return Number.NaN; } return Number.parseInt(cookie.slice(CHUNKED_COOKIE.length)); // No upper bound check — attacker controls this value } ``` This value is consumed without validation in the cleanup loop of `setChunkedCookie()` at `src/utils/cookie.ts:182-190`: ```typescript const previousCookie = getCookie(event, name); // reads from request headers if (previousCookie?.startsWith(CHUNKED_COOKIE)) { const previousChunkCount = getChunkedCookieCount(previousCookie); if (previousChunkCount > chunkCount) { for (let i = chunkCount; i <= previousChunkCount; i++) { deleteCookie(event, chunkCookieName(name, i), options); // Each deleteCookie → setCookie → scans ALL existing set-cookie headers } } } ``` The same issue exists in `deleteChunkedCookie()` at `src/utils/cookie.ts:227-232`: ```typescript const chunksCount = getChunkedCookieCount(mainCookie); if (chunksCount >= 0) { for (let i = 0; i < chunksCount; i++) { deleteCookie(event, chunkCookieName(name, i + 1), serializeOptions); } } ``` **The exploit chain through sessions:** 1. Attacker sends `Cookie: h3=__chunked__999999` to any session-using endpoint 2. `getSession()` (`src/utils/session.ts:83`) calls `getChunkedCookie(event, "h3")` (line 124) 3. `getChunkedCookie()` returns `undefined` — the early return at line 153 fires because no actual chunk cookies (e.g., `h3.1`) exist in the request 4. Since `sealedSession` is undefined, `session.id` remains empty (line 140), triggering `updateSession()` (line 143) 5. `updateSession()` calls `setChunkedCookie()` with the newly sealed session value (line 179) 6. Inside `setChunkedCookie()`, `getCookie(event, name)` re-reads the original request cookie `__chunked__999999` at line 182 7. `previousChunkCount` = 999999, `chunkCount` = 1 (new sealed session is small) 8. The cleanup loop runs 999,998 iterations, each calling `deleteCookie()` → `setCookie()` 9. Each `setCookie()` call reads ALL existing `set-cookie` response headers via `getSetCookie()` (line 91) and iterates through them for deduplication (lines 100-106) 10. This creates O(n²) complexity — approximately 10¹² operations for n=999999 **Key observation:** While `getChunkedCookie()` has an early-return optimization (line 153) that prevents it from looping on missing chunks, the cleanup loops in `setChunkedCookie()` and `deleteChunkedCookie()` have no such protection and run unconditionally for the full claimed chunk count. ## PoC **Prerequisites:** An h3 application with any endpoint using `getSession()` or `useSession()`. Example minimal server: ```typescript import { H3 } from "h3"; import { getSession } from "h3"; const app = new H3(); app.get("/dashboard", async (event) => { const session = await getSession(event, { password: "my-secret-password-at-least-32-chars-long!", }); return { user: session.data.user || "anonymous" }; }); export default app; ``` **Attack (single request, no authentication):** ```bash # This single request will hang the server process curl -H 'Cookie: h3=__chunked__999999' http://localhost:3000/dashboard ``` For a less extreme but still impactful test: ```bash # ~100K iterations — will take several seconds and block all other requests curl -H 'Cookie: h3=__chunked__100000' http://localhost:3000/dashboard ``` The `deleteChunkedCookie()` path is exploitable via `clearSession()`: ```typescript app.post("/logout", async (event) => { await clearSession(event, { password: "my-secret-password-at-least-32-chars-long!", }); return { ok: true }; }); ``` ```bash curl -X POST -H 'Cookie: h3=__chunked__999999' http://localhost:3000/logout ``` ## Impact - **Complete Denial of Service**: A single unauthenticated request with a 27-byte cookie header can hang the server process indefinitely. Node.js is single-threaded, so this blocks all request handling. - **No authentication required**: The attack only requires the ability to send HTTP requests with a crafted cookie header. - **Minimal attacker effort**: The payload is trivially small (`Cookie: h3=__chunked__999999`), making it easy to automate or repeat. - **Wide attack surface**: Any endpoint in the application that uses `getSession()`, `useSession()`, or `clearSession()` is vulnerable. Session usage is extremely common in web applications. - **Amplification**: The ratio of attacker input (27 bytes) to server work (billions of operations) is extreme. ## Recommended Fix Add a maximum chunk count constant and validate in `getChunkedCookieCount()`: ```typescript const MAX_CHUNKED_COOKIE_COUNT = 100; function getChunkedCookieCount(cookie: string | undefined): number { if (!cookie?.startsWith(CHUNKED_COOKIE)) { return Number.NaN; } const count = Number.parseInt(cookie.slice(CHUNKED_COOKIE.length)); if (Number.isNaN(count) || count < 0 || count > MAX_CHUNKED_COOKIE_COUNT) { return Number.NaN; } return count; } ``` This clamps the parsed count at a safe maximum. Since each chunk can hold ~4000 bytes and 100 chunks would allow ~400KB of cookie data (far beyond any practical limit), `MAX_CHUNKED_COOKIE_COUNT = 100` is generous while eliminating the DoS vector. Additionally, the callers should be updated to handle `NaN` safely. The cleanup loop in `setChunkedCookie()` already handles this correctly since `NaN > chunkCount` is false, so the loop won't execute. The `deleteChunkedCookie()` loop also handles it since `NaN >= 0` is false.

Source published
23 Mar 2026 21:44:55 UTC
Source modified
1 Oct 2026 00:00:05 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 12 Nov 08:00 UTC · 274 of 275 readable · 272 fully read · documentation 469/469 current