Code injection during object deserialization
ID |
javascript.code_injection_with_deserialization |
Severity |
critical |
Resource |
Injection |
Language |
JavaScript |
Tags |
CWE:502, NIST.SP.800-53, OWASP:2021:A8, PCI-DSS:6.5.1 |
Description
Improper deserialization of untrusted data, possibly allowing code injection attacks.
Deserialization vulnerabilities arise when an application deserializes user-controlled data without proper validation, potentially allowing attackers to instantiate unexpected objects or execute crafted functions leading to arbitrary code execution or system exploits.
Rationale
After detecting the vulnerable site, attackers typically craft a payload that is serialized and sent to the application. If the application deserializes this payload without validation, attackers can modify the expected data structure to escalate privileges or perform unwanted actions.
In the worst case, the attackers inject an object of an unexpected type, triggering chosen code execution. This could be used to exfiltrate internal server’s data, install malware e.g. to install and persist crypto-miners, or run a reverse shell.
Certain common JavaScript serialization modules (such as node-serialize, serialize-to-js, or js-yaml) provide a framework for arbitrary object persistence (without mapping information), serializing objects to formats like JSON or YAML. Such frameworks allow to serialize functions, to reconstruct object instances properly during deserialization.
If the application deserializes content from untrusted sources (e.g. in a REST framework), and if an attacker can provide the content to be deserialized, he/she may be able to execute arbitrary code on the server, including opening a reverse shell to launch commands.
The following example uses the load()
function of the js-yaml
that it is vulnerable to code injection during deserialization:
const express = require('express');
const app = express();
const yaml = require("js-yaml");
app.post('/user', function(req, res) {
// FLAW - vulnerable to code injection during deserialization
var usrInfo = yaml.load( req.params.userData );
// ...
});
Remediation
If possible, do not deserialize content to objects when the source is not fully trusted.
Mitigating deserialization vulnerabilities involves several key practices:
-
Avoid Deserialization of Untrusted Input: The most secure approach is to avoid deserializing any untrusted data outright. If deserialization is necessary, consider other safe data formats such as JSON or XML with strict schema validation.
-
Use Secure Libraries: Employ libraries or frameworks that provide secure deserialization mechanisms.
-
Validation and Whitelisting: Implement strict validation. Use a whitelist approach where only the expected, permitted, and safe classes are allowed to be deserialized.
The vulnerable code shown before could be fixed by using the safeLoad()
function instead:
const express = require('express');
const app = express();
const yaml = require("js-yaml");
app.post('/user', function(req, res) {
// FIXED - added to prevent code injection
var usrInfo = yaml.safeLoad( req.params.userData );
// ...
});
Configuration
The detector has the following configurable parameters:
-
sources
, that indicates the source kinds to check. -
neutralizations
, that indicates the neutralization kinds to check.
Unless you need to change the default behavior, you typically do not need to configure this detector.
References
-
CWE-502 : Deserialization of Untrusted Data.
-
OWASP - Top 10 2021 Category A08 : Software and Data Integrity Failures.
-
Deserialization Cheat Sheet - OWASP Cheat Sheet Series.
-
Exploiting Node.js deserialization bug for Remote Code Execution.