Constructing arguments of system commands from user input is security-sensitive. It has led in the past to the following vulnerabilities:
Arguments of system commands are processed by the executed program. The arguments are usually used to configure and influence the behavior of the
programs. Control over a single argument might be enough for an attacker to trigger dangerous features like executing arbitrary commands or writing
files into specific directories.
Ask Yourself Whether
- Malicious arguments can result in undesired behavior in the executed command.
- Passing user input to a system command is not necessary.
There is a risk if you answered yes to any of those questions.
Recommended Secure Coding Practices
- Avoid constructing system commands from user input when possible.
- Ensure that no risky arguments can be injected for the given program, e.g., type-cast the argument to an integer.
- Use a more secure interface to communicate with other programs, e.g., the standard input stream (stdin).
Sensitive Code Example
Arguments like -delete
or -exec
for the find
command can alter the expected behavior and result in
vulnerabilities:
import (
"fmt"
"net/http"
"os/exec"
)
func ListDirectory(w http.ResponseWriter, req *http.Request) {
dirs := req.URL.Query()["dir"]
output, _ := exec.Command("/usr/bin/find", dirs...).CombinedOutput() // Sensitive
fmt.Fprintf(w, "Output: %s", output)
}
Compliant Solution
Use an allow-list to restrict the arguments to trusted values:
import (
"fmt"
"net/http"
"os/exec"
)
var allowed = map[string]bool{"/tmp": true}
func ListDirectory(w http.ResponseWriter, req *http.Request) {
dirs := req.URL.Query()["dir"]
for _, dir := range dirs {
if _, ok := allowed[dir]; !ok { // Validator
fmt.Fprintf(w, "Error")
return
}
}
output, _ := exec.Command("/usr/bin/find", dirs...).CombinedOutput()
fmt.Fprintf(w, "Output: %s", output)
}
See