CodeQL queries 1.23

Skip to end of metadata
Go to start of metadata

Name: Prototype pollution

Description: Recursively merging a user-controlled object into another object can allow an attacker to modify the built-in Object prototype.

ID: js/prototype-pollution

Kind: path-problem

Severity: error

Precision: high

Query: PrototypePollution.ql
/**
 * @name Prototype pollution
 * @description Recursively merging a user-controlled object into another object
 *              can allow an attacker to modify the built-in Object prototype.
 * @kind path-problem
 * @problem.severity error
 * @precision high
 * @id js/prototype-pollution
 * @tags security
 *       external/cwe/cwe-250
 *       external/cwe/cwe-400
 */

import javascript
import semmle.javascript.security.dataflow.PrototypePollution::PrototypePollution
import DataFlow::PathGraph
import semmle.javascript.dependencies.Dependencies

from
  Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string moduleName,
  Locatable dependencyLoc
where
  cfg.hasFlowPath(source, sink) and
  sink.getNode().(Sink).dependencyInfo(moduleName, dependencyLoc)
select sink.getNode(), source, sink,
  "Prototype pollution caused by merging a user-controlled value from $@ using a vulnerable version of $@.",
  source, "here", dependencyLoc, moduleName

Most JavaScript objects inherit the properties of the built-in Object.prototype object. Prototype pollution is a type of vulnerability in which an attacker is able to modify Object.prototype. Since most objects inherit from the compromised Object.prototype, the attacker can use this to tamper with the application logic, and often escalate to remote code execution or cross-site scripting.

One way to cause prototype pollution is through use of an unsafe merge or extend function to recursively copy properties from an untrusted source object. Such a call can modify any object reachable from the destination object, and the built-in Object.prototype is usually reachable through the special properties __proto__ and constructor.prototype. An attacker can abuse this by sending an object with these property names and thereby modify Object.prototype.

Recommendation

Update your library dependencies in order to use a safe version of the merge or extend function. If your library has no fixed version, switch to another library.

Example

In the example below, the untrusted value req.query.prefs is parsed as JSON and then copied into a new object:

app.get('/news', (req, res) => {
  let prefs = lodash.merge({}, JSON.parse(req.query.prefs));
})

Prior to lodash 4.17.11 this would be vulnerable to prototype pollution. An attacker could send the following GET request:

GET /news?prefs={"constructor":{"prototype":{"xxx":true}}}

This causes the xxx property to be injected on Object.prototype. Fix this by updating the lodash version:

{
  "dependencies": {
    "lodash": "^4.17.12"
  }
}

Note that some web frameworks, such as Express, parse query parameters using extended URL-encoding by default. When this is the case, the application may be vulnerable even if not using JSON.parse. The example below would also be susceptible to prototype pollution:

app.get('/news', (req, res) => {
  let config = lodash.merge({}, {
    prefs: req.query.prefs
  });
})

In the above example, an attacker can cause prototype pollution by sending the following GET request:

GET /news?prefs[constructor][prototype][xxx]=true
References