Back to Blog
critical SEVERITY9 min read

How Prototype Pollution happens in Node.js protobufjs and how to fix it

CVE-2023-36665 is a critical prototype pollution vulnerability in protobufjs that allows attackers to corrupt JavaScript's Object prototype by crafting malicious protobuf messages. The vulnerability existed in protobufjs 6.11.3 and was resolved by upgrading to 6.11.4 (and 7.2.5 for the v7 branch). Applications that parse user-supplied protobuf data are directly at risk of runtime behavior manipulation, privilege escalation, or denial of service.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published August 26, 2026•Reviewed August 26, 2026

Answer Summary

CVE-2023-36665 is a critical prototype pollution vulnerability (CWE-1321) in the Node.js library protobufjs, affecting versions prior to 6.11.4 and 7.2.5. An attacker can craft a protobuf message containing a specially constructed field name such as `__proto__` or `constructor` to overwrite properties on JavaScript's global `Object.prototype`, potentially enabling privilege escalation, property injection, or denial of service. The fix is to upgrade protobufjs from 6.11.3 to 6.11.4 (or from any v7 release to 7.2.5), which adds sanitization of user-controlled field names during message deserialization to prevent prototype chain manipulation.

Vulnerability at a Glance

cweCWE-1321
fixUpgrade protobufjs from 6.11.3 to 6.11.4 in package.json and package-lock.json, which adds field name validation to block prototype-polluting keys
riskAttackers can overwrite Object.prototype properties via crafted protobuf messages, enabling privilege escalation, property injection, or DoS
languageJavaScript / Node.js
root causeprotobufjs 6.11.3 did not sanitize user-controlled field names during message deserialization, allowing keys like `__proto__` to traverse the prototype chain
vulnerabilityPrototype Pollution

How Prototype Pollution Happens in Node.js protobufjs and How to Fix It


Vulnerability at a Glance

Field Detail
Vulnerability Prototype Pollution
CVE CVE-2023-36665
CWE CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes
Severity Critical
Affected Package protobufjs 6.11.3 (and < 7.2.5 in the v7 branch)
Fixed In protobufjs 6.11.4 / 7.2.5
Language JavaScript / Node.js

Introduction

The package-lock.json in this project pinned protobufjs to version 6.11.3 — a version that contains a critical prototype pollution vulnerability. Any code path that deserializes a user-supplied protobuf message using this library becomes an entry point for an attacker to silently overwrite properties on Object.prototype, the root of JavaScript's object inheritance chain. Because prototype properties are inherited by every object in a Node.js process, a single malicious message can alter the behavior of unrelated parts of the application without any obvious error being thrown.

The vulnerability was assigned CVE-2023-36665 and rated Critical. It was detected by Trivy scanning the project's package-lock.json dependency tree.


The Vulnerability Explained

What Is Prototype Pollution?

In JavaScript, every object inherits properties from Object.prototype. If an attacker can cause your code to execute something equivalent to:

obj["__proto__"]["isAdmin"] = true;

…then every object in the running process suddenly has isAdmin === true, because they all share the same prototype. This is prototype pollution.

How protobufjs 6.11.3 Was Vulnerable

Protocol Buffers (protobuf) is a binary serialization format developed by Google. The protobufjs library parses .proto schema definitions and decodes binary or JSON-encoded protobuf messages into JavaScript objects. During decoding, the library maps protobuf field names to JavaScript object keys.

In protobufjs versions before 6.11.4, the deserialization logic did not adequately guard against field names that are special in JavaScript's prototype chain — specifically keys like:

  • __proto__
  • constructor
  • prototype

If an attacker crafts a protobuf message (or a JSON-encoded protobuf payload) containing one of these reserved names as a field, the library would process it and assign the attacker-controlled value directly onto the target object using bracket notation — effectively walking up the prototype chain rather than setting an own property.

A simplified illustration of the dangerous pattern inside the library's object-building logic (before the fix):

// Vulnerable pattern — no key sanitization
function setProperty(obj, key, value) {
  obj[key] = value; // If key is "__proto__", this pollutes Object.prototype
}

When key is "__proto__" and value is { "isAdmin": true }, the assignment obj["__proto__"] = { "isAdmin": true } modifies the prototype of obj, not a property named __proto__. In V8 (Node.js's JavaScript engine), this propagates to Object.prototype itself.

Concrete Attack Scenario

Consider an application that:
1. Accepts binary or JSON protobuf messages from users over an HTTP API.
2. Uses protobufjs 6.11.3 to decode those messages.
3. Later checks if (request.user.isAdmin) to gate privileged operations.

An attacker sends a crafted protobuf message with a field named __proto__ containing { "isAdmin": true }. After protobufjs decodes this message, Object.prototype.isAdmin is now true for the entire process lifetime. Every subsequent request.user.isAdmin check — even for unauthenticated users — returns true, granting full administrative access.

Beyond privilege escalation, prototype pollution can be chained with other vulnerabilities (e.g., property injection into child_process.spawn option objects) to achieve Remote Code Execution.

What Was Pinned in This Project

The vulnerable version was declared in two places in package-lock.json and package.json:

// package.json — before fix
"protobufjs": "^6.11.2"
// package-lock.json — node_modules/protobufjs — before fix
"version": "6.11.3",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
"integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg=="

Because the semver range was ^6.11.2 (which resolves to >=6.11.2 <7.0.0), the lock file had resolved to 6.11.3 — the vulnerable release. The range would allow 6.11.4, but the lock file pinned the older version until explicitly updated.


The Fix

The fix upgrades protobufjs from 6.11.3 to 6.11.4 in both package.json and package-lock.json. The 6.11.4 release adds sanitization of user-controlled field names during protobuf message deserialization, rejecting or neutralizing keys that would traverse the prototype chain.

Before and After: package.json

- "protobufjs": "^6.11.2",
+ "protobufjs": "^6.11.4",

Bumping the lower bound of the range to ^6.11.4 ensures that npm install will never again resolve to a vulnerable patch version within the 6.x series.

Before and After: package-lock.json (node_modules/protobufjs)

- "version": "6.11.3",
- "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
- "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
+ "version": "6.11.4",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+ "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
  "hasInstallScript": true,
+ "license": "BSD-3-Clause",

The integrity hash change is critical — it confirms that npm will now verify the downloaded tarball against the hash of the patched release, not the vulnerable one. Notably, 6.11.4 also formally declares its BSD-3-Clause license in the lock file metadata, a minor housekeeping improvement included in that release.

The same version bump appears in the flattened dependencies section of package-lock.json (the legacy npm v2 format section), ensuring consistency across both lock file representations:

  "protobufjs": {
-   "version": "6.11.3",
-   "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
-   "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==",
+   "version": "6.11.4",
+   "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+   "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",

Why Both Files Must Be Updated

Many developers assume that changing package.json alone is sufficient. It is not. The package-lock.json is the authoritative source of truth for npm ci (used in CI/CD pipelines). If only package.json is updated, a npm ci run in a deployment pipeline will still install 6.11.3 because it reads the exact version from the lock file. Updating both files is mandatory to guarantee the fix takes effect in all environments.


Key Takeaways

  • protobufjs 6.11.3 treats user-controlled field names as trusted object keys — any field named __proto__ in a parsed message can overwrite Object.prototype for the entire process.
  • Both package.json and package-lock.json must be updated — updating only package.json leaves npm ci deployments still installing the vulnerable 6.11.3.
  • The integrity hash change from sha512-xL96... to sha512-5kQW... is the cryptographic proof that the patched tarball is now being used, not the vulnerable one.
  • Prototype pollution via deserialization is silent — it does not throw errors, making it especially dangerous in production systems where observability depends on normal exception handling.
  • Semver ranges like ^6.11.2 do not automatically protect you — the lock file freezes the resolved version until you explicitly regenerate it with a patched release.

How Orbis AppSec Detected This

  • Source: User-controlled binary or JSON protobuf message payload supplied to protobufjs deserialization logic in any code path importing the protobufjs package declared in package-lock.json.
  • Sink: The protobufjs internal object-building routine that assigns decoded field names as JavaScript object keys without sanitizing prototype-polluting names (__proto__, constructor, prototype), present in node_modules/protobufjs version 6.11.3.
  • Missing control: No validation or rejection of field names that correspond to JavaScript prototype chain properties before dynamic property assignment inside the deserialization library.
  • CWE: CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution').
  • Fix: Both package.json and package-lock.json were updated to resolve protobufjs at version 6.11.4, which includes internal key sanitization that blocks prototype-polluting field names during message decoding.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2023-36665 is a sharp reminder that deserialization libraries are a high-value attack surface. protobufjs is trusted to faithfully translate binary protobuf data into JavaScript objects — but in version 6.11.3, that trust extended too far, allowing attacker-controlled field names to silently corrupt the JavaScript runtime's prototype chain. The fix is surgical: upgrade to 6.11.4, update both manifest files, and verify the new integrity hash is in place.

More broadly, every library that maps external data to object keys deserves scrutiny. Prototype pollution is notoriously difficult to detect at runtime because it produces no immediate error — its effects manifest elsewhere in the application, often in security checks or configuration reads that suddenly return attacker-controlled values. Automated scanning of your dependency tree, as demonstrated here with Trivy, is the most reliable way to catch these issues before they reach production.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1402

Related Articles

high

CVE-2026-4800: lodash Template Imports Allow Code Execution

CVE-2026-4800 affects lodash's `_.template()` templating API, where untrusted input reaching the `imports` option can lead to arbitrary code execution. The fix was shipped as a dependency upgrade from lodash 4.17.21 to 4.18.1 in the project's lockfile, though the PR itself notes it was never verified against the actual code paths in this repository.

critical

proxy-addr 2.0.7 IP Spoofing: CVE-2026-90711 Trust Bypass

A critical vulnerability in proxy-addr 2.0.7 allowed attackers to spoof client IP addresses by manipulating X-Forwarded-For headers when the trust chain evaluation contained specific misconfigurations. The fix in version 2.0.8 hardens the trust evaluation logic to prevent IP address falsification in Express.js applications relying on this common middleware dependency.

high

TweenMax `_applyCycle` Prototype Pollution via vars.cycle Keys

A bundled copy of the TweenMax animation library copied attacker-influenceable `vars.cycle` property names straight onto a tween configuration object using an unguarded `for...in` loop, so a key named `__proto__`, `constructor`, or `prototype` was written through to the object's prototype chain. The fix adds an explicit key denylist to both copies of the `_applyCycle` helper so those three names are skipped during the merge. No CVE or GHSA is assigned; the issue is tracked as CWE-1321 (Improperl

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

sanitizeUnicodeInput(): Fullwidth U+ Bypasses Codepoint Validation

The `sanitizeUnicodeInput()` helper used by the project character-range settings screen rewrote `U+` prefixes to `0x` and called `parseInt()`, but never normalized its argument first. Compatibility-equivalent forms such as fullwidth `U+`, superscript digits, or mathematical alphanumerics never matched the `/U\+/gi` regex, fell through to the `else return inputString` branch, and were handed back to callers verbatim as "sanitized" values. The fix inserts a `String.prototype.normalize('NFKC')` pas