OS Command Injection in Python
Vulnerable example
The following snippet contains a Flask web application written in Python that executes the nslookup
command to resolve the host supplied by the user.
@app.route("/dns")
def page():
hostname = request.values.get(hostname)
cmd = 'nslookup ' + hostname
return subprocess.check_output(cmd, shell=True)
Since the hostname
is simply appended to the command and executed on a subshell with shell=True
, an attacker could stack another command using ;
in the file_path
GET parameter to inject additional commands. The screenshot shows an attack injecting the cat
command to disclose /etc/passwd
.
Prevention
Python has native APIs to execute commands. Some of them accept the shell
argument that might be set as True
to accept the command as a single string. This should be avoided, with commands being passed as a list of arguments, whenever possible.
Some methods in the os
library only accept the commands argument as single strings and are prone to introducing an injection vulnerability. These functions should not be used; however, in cases where it is unavoidable, they should be used very carefully by escaping or filtering out the characters against an allow list (such as filtering out everything that is not alphanumeric).
os.system(cmd)
os.popen(cmd, ...)
The recommended approach is to execute commands using the subprocess
API, passing the command as a list of argument strings with the shell
option set to False
.
subprocess.call(args, ..., shell=False)
subprocess.run(args, ..., shell=False)
subprocess.Popen(args, ..., shell=False)
subprocess.check_output(args, ..., shell=False)
subprocess.check_call(args, ..., shell=False)
Setting shell
as True
to pass the command as single string introduces the vulnerability.
subprocess.Popen('nslookup ' + hostname, ... , shell=True) # WRONG
Passing the command as a list of arguments is the safer approach that should always be used, but it might be vulnerable to argument injection depending on the binary.
subprocess.Popen([ 'nslookup', hostname ], ... , shell=False)
References
https://cwe.mitre.org/data/definitions/78.html https://www.owasp.org/index.php/Command_Injection https://docs.python.org/3/library/os.html https://docs.python.org/3/library/subprocess.html