OS Command Injection in NodeJS
Vulnerable example
Node.js provides several ways to execute external programs as part of the child_process
package. The exec
method spawns a shell then executes the command within that shell. It is important to never pass unsanitized user input to this function. Similarly, the two other variants execFile
and spawn
have a shell
option (which defaults to false
). If enabled, the method executes the provided command inside a system shell; in these cases, it is important to never pass unsanitized user input to the function.
The following example uses the execSync
(synchronous version of exec
) to run a system command with unsanitized user input:
const output = child_process.execSync(`ping -c 1 '${destination}'`);
Note that wrapping destination
in single quotes is not a sufficient countermeasure.
Prevention
Since Node.js lacks a proper shell-escaping mechanism, when possible, refrain from using a proper shell invocation and leverage Node.js APIs. If this is not possible, execution of external commands should be handled by using other methods (execFile
and spawn
) to directly invoke the desired command, instead of by invoking a system shell. By passing individual program arguments to this method, the argument injection is avoided. For example:
const output = child_process.spawnSync('ping', ['-c', '1', destination]);
The value of destination
is always passed as a single argument to ping
.
References
https://cwe.mitre.org/data/definitions/78.html https://www.owasp.org/index.php/Command_Injection