OS Command Injection From A Python/Django Perspective
Welcome to the third article in the series ‘OWASP Top-10 From A Python/Django Perspective’. Please check out my previous two articles on SQL Injection and LDAP Injection if you have not yet. In this article, we will look into OS Command Injection.
What is OS Command Injection?
OS Command Injection is a vulnerability in which malicious data is injected into the application and sent to the Operating System. The OS then executes the malicious command leading to terrible consequences.
This kind of vulnerability may exist in applications which needs to execute OS Commands as part of its functionality. For example, an application that performs Server Monitoring and provides information such as CPU, Memory, Network and Disk utilization would need to execute OS commands on that server to get those results. An application that converts video files to another format would also need to execute commands (e.g. using FFmpeg).
Let’s consider a very simple application that performs a DNS lookup of a domain using the nslookup command. The application prompts for the domain name. When we enter the Domain Name, it returns the output of the DNS lookup containing the IP address. We will be using subprocess to execute the command.
Let’s run this small program and check the output.
When we entered rqsolutions.com it returned the associated IP Address as expected.
But, what if we entered rqsolutions.com ; ls -la ??
The command at the backend would be nslookup rqsolutions.com ; ls -la which is a valid UNIX command. Let’s try that as well.
You can see that the OS executed both nslookup and ls and returned the output.
What if instead of ls -la the command we entered was rm -rf *.* ?? It would delete all the files within the directory and wreak havoc. This is a simple example of OS Command Injection.
How to prevent?
Apart from validating the input, there are two ways to prevent this. The first option is to quote the input using shlex.quote() as shown below.
This will ensure that the input is quoted and the OS will not consider anything within the input as commands that needs to be executed. The command will now look like:
nslookup ‘rqsolutions.com ; ls -la’
Let’s input the same data and check the result.
The OS did not execute the command and the program returned an error preventing any malicious activity.
The second option is to split the command into a list of strings using shlex.split().
When we input the same data as above, the safe_command will look like:
['nslookup', 'rqsolutions.com', ';', 'ls', '-la']
This list of strings is then executed by subprocess. Let’s check the output for the same.
The OS did not execute the command and the program returned an error preventing any malicious activity.
Please note that the second option of using shlex.split() is recommended as shell=True is omitted in subprocess and is considered more safe. Additionally, avoid using the deprecated os module for executing commands.
How to detect?
OS Command injection can be detected in two ways:
- Performing Source Code Security Review
- Performing Web Application Vulnerability Scanning
Performing Source Code Security Review:
Source code security review is also known as Static Analysis Security Testing (SAST). Follow the process below:
- Check if the deprecated os module is used
- Check if subprocess is used with shell=True
- If subprocess needs to be used with shell=True, then check if shlex.quote() is used as a prevention control
- Check if shlex.split() is used as a prevention control
Performing Web Application Vulnerability Scanning
Web Application Vulnerability Scanners can identify OS Command injection vulnerability when the application is up and running. This kind of testing is also known as Dynamic Application Security Testing (DAST).
Good article, would have been nice if you talked about Static Analysis tools for Python. Once can configure tool like: https://github.com/python-security/pyt to run as pre-commit hook. This way every-time you did a "git commit" it would let you know if there are any vulnerability detected in the code.