unsafe-child-process¶
Calling the child_process.exec()
function, or the child_process.execFile()
or child_process.spawn()
functions with the {shell:true}
option, execute commands in a newly spawned shell. When executed in a shell, the command can contain shell meta-characters that allow running multiple commands unexpectedly. For example, the command child_process.exec('ls;printenv')
will run both ls
and printenv
.
It is dangerous to pass user controlled input to any exec
function, but it is particularly dangerous in these three cases. If user controlled input is passed into the command string, then attackers can use this to execute malicious commands.
Examples¶
Insecure Example
let userInput = req.query.param1 // "; printenv"
// Example 1
child_process.exec('ls ' + userInput) // executes "ls ; printenv"
// Example 2
child_process.spawn('ls ' + userInput, {shell: true}) // executes "ls ; printenv"
// Example 3
child_process.spawn('ls', [userInput], {shell: true}) // executes "ls ; printenv"
// Example 4
child_process.execFile('ls ' + userInput, {shell: true}) // executes "ls ; printenv"
// Example 5
child_process.execFile('ls', [userInput] {shell: true}) // executes "ls ; printenv"
Secure Example
let userInput = req.query.param1 // "; printenv"
// Example 1 - Still dangerous, but less bad
child_process.spawn('ls ' + userInput) // throws an error
// Example 1.5 - Why it's still dangerous
let userInput = req.query.param1 // "../../../../etc/passwd"
child_process.spawn('cat mydir/' + userInput) // no metacharacters, so this executes "cat mydir/../../../../etc/passwd
// Example 2 - Still dangerous, but less bad
child_process.execFile('ls ' + userInput) // throws an error
// Example 3 - Best Practice
safeExec(untrustedUserInput) {
let userDefinedIndex = parseInt(untrustedUserInput)
let allowedCommandOptions = [
['.'],
['-la'],
['someSubdirectory'],
['-la', 'someSubdirectory]
]
child_process.spawn('ls', allowedCommandOptions[userDefinedIndex])
}