vigour-io / unflatten

Opposite of flatten-obj
ISC License
2 stars 2 forks source link

fixed prototype pollution #5

Open Sampaguitas opened 2 years ago

Sampaguitas commented 2 years ago

Description

All versions of unflatten are vulnerable to prototype pollution. The function unflatten does not restrict the modification of an Object's prototype, which may allow an attacker to add or modify an existing property that will exist on all objects.

function unflatten (obj, opts) {
  var separator = '.'
  var objectMode = false
  if (typeof opts === 'string') {
    separator = opts
  } else if (typeof opts === 'boolean') {
    objectMode = opts
  } else if (opts) {
    separator = opts.separator || separator
    objectMode = opts.objectMode
  }
  const dotSep = (separator === '.')
  var re = new RegExp(separator, 'g')
  var newObj = {}
  for (let path in obj) {
    if (/__proto__/.test(path) === true) {
      break;
    }
    if (objectMode) {
      _setWith(newObj, dotSep ? path : path.replace(re, '.'), obj[path], Object)
    } else {
      _set(newObj, dotSep ? path : path.replace(re, '.'), obj[path])
    }
  }
  return newObj
}

Proof of Concept

  1. Create the following PoC file:
// poc.js
const unflatten = require("unflatten");

const obj = {};

console.log(`[+] Before prototype pollution : ${obj.polluted}`);
unflatten ({ '__proto__.polluted': true })
console.log(`[+] After prototype pollution : ${obj.polluted}`);
  1. Execute the following commands in terminal:
npm i unflatten # Install vulnerable package
node poc.js #  Run the PoC
  1. Check the Output:
[+] Before prototype pollution : undefined
[+] After prototype pollution : true

Impact

This vulnerability would allow an attacker to access sensitive information and could potentially lead to Remote Code Execution.

Proof of Fix

  1. add a condition in the for loop to escape strings containing proto:
for (let path in obj) {
    if (/__proto__/.test(path) === true) {
      break;
    }
    if (objectMode) {
      _setWith(newObj, dotSep ? path : path.replace(re, '.'), obj[path], Object)
    } else {
      _set(newObj, dotSep ? path : path.replace(re, '.'), obj[path])
    }
  }
  1. Execute the following command:
node poc.js #  Run the PoC
  1. Check the Output:
[+] Before prototype pollution : undefined
[+] After prototype pollution : undefined