A primer on OS Command Injection Attacks
Reading Time: 8 Minutes
Introduction
Command Injection or OS command Injection is a category of injection vulnerabilities. It allows an attacker to execute arbitrary operating system commands on the server that the application is run by. That could typically lead to the full compromise of the web application and its data.
These kinds of attacks are possible only if the web application source code includes operating system command calls and user input is used in the command calls.
For example, a web application written in PHP may use shell_exec or exec to execute commands directly on the back-end server.
As they are not language-specific, command injection vulnerabilities may appear in all languages that let you perform a system shell command such as C, PHP, Java, JS, etc.
By themselves, OS command injection vulnerabilities do not lead to the full compromise of the system, since the injected commands are executed with the privileges of the vulnerable server. Therefore, an attacker should leverage the OS command injection to compromise other parts of the web application, such as escalating privileges for getting access to higher-level accounts or even exploiting other vulnerabilities to pivot the attack to other systems of the vulnerable server.
Typically, these vulnerabilities exist due to insufficient input validation and input sanitization.
How Command Injection Works
- An attacker should first identify the point of injection to allow them to insert the malicious payload into the command.
- By attempting to append the command through various injection methods to see if the command output changed from the usual result. If it changes, the vulnerability is exploited successfully. This may not be true for more advanced OS command injection vulnerabilities which need the utilization of fuzzing or code review (if possible) to identify potential command injection vulnerabilities, and then gradually build the payload until the command injection is successful.
- Then the attacker would execute arbitrary commands to collect the data needed to pivot into other systems of the vulnerable application server or steal information/data directly from the injection point.
Identifying and executing arbitrary commands
The below PHP application code shows the output of the ping command in the web application:
The application is using the shell_exec (PHP function) which executes the ping command via shell and returns the complete output as a string.
The user needs to input their IP address and the web application sends ICMP pings to the address provided and then displays the output of the ping command.
The IP address is passed using the HTTP “GET” method and then used in the command line.
The developer of the application does not perform any input validation.
See Also: So you want to be a hacker?
Complete Offensive Security and Ethical Hacking Course
Normal flow of the application’s GET request
The normal flow of the application using the GET request and the URL looks like the following:
http://vulnerableapp.com/ping.php?address=8.8.8.8
The PHP function (shell_exec) executes the following OS command: ping -n 1 <IP_address>, and displays the command output.
Manipulating the GET request
The attacker can then manipulate the GET request (using a web proxy that can intercept HTTP requests e.g. Burp, to bypass any FE validation), and add extra parameters using command operators, in this case, the &&.
Attacker’s manipulated URL looks like:
http://example.com/ping.php?address=8.8.8.8&&dir
Or
Encoding the command operator using URL-encoded format to bypass filters:
http://example.com/ping.php?address=8.8.8.8%26%26dir
As a result, the vulnerable application executes the additional command (dir) and displays the command output, which in this case is a directory listing.
Trending: Recon Tool: BF ActiveSub
Command Injection Operators – Cheatsheet
There are many operators to inject an arbitrary command. The most used for Linux is the semicolon (;), and for Windows, the ampersand (&).
The following cheats-sheet shows some of the most used operators’ names, characters, their URL-encoded characters, and what commands will be executed.
For Windows and Unix-based systems:
Injection Operator | Injection Character | URL-Encoded Character | Executed Command |
Ampersand | & | %26 | Both (second output generally shown first) |
AND | && | %26%26 | Both (only if first succeeds) |
Pipe | | | %7c | Both (only second output is shown) |
OR | || | %7c%7c | Second (only if first fails) |
New Line | \n | %0a | Both |
Semicolon | ; | %3b | Both |
As you can see, there are various operators to choose from for any scenario. There are more than the provided ones but those could be considered the most wildly known and used.
Bypassing filtered characters and commands
Even if software developers attempt to secure a web application against injections, it may still be exploitable if it’s not properly coded. The developer may utilize a mitigation for injection vulnerabilities and add blacklisted characters and words on the back-end to detect injection attempts and deny the request.
A PHP code example using mitigation injected commands, blacklisting characters in order to deny any request that may contain them.
Bypassing Blacklisted Spaces
Space is a commonly blacklisted character, especially if the input should not contain any spaces. However, there are ways to add a space character without actually using the space character.
{IFS}
One way to bypass blacklisted spaces is using the Linux Environment Variable {IFS}, which its default value is a space and a tab.
Taking into consideration our previous example a space could be added after the command:
http://example.com/ping.php?address=8.8.8.8&&cd{IFS}<name_of_directory>
Tabs
Another way is using tabs instead of spaces, which is a technique that may work as both Linux and Windows accept commands with tabs between arguments. The tab character is %09.
Using the previous example we can replace {IFS} with the tab character:
http://example.com/ping.php?address=8.8.8.8&&cd%09<name_of_directory>
Bypass blacklisted characters
Upon finding the vulnerable injection point, to build the injection command and find what is a filter or not, it needs to be gradual in order to understand what is filtered and what is not.
Depending on the web application, bypassing the filtering of the operators could be considered by just adding the encoded operators (& -> %26) instead of their injection character. Trying every possible operator could lead to successfully injecting a command. Another option is to use the new-line(\n) operator which is usually not blacklisted since it may be needed in the payload itself.
Besides injection operators, a commonly blacklisted character is the slash(/) and backslash(\), as they are necessary for specifying directories in Windows and Linux.
Linux
Among many techniques to utilize slashes in a payload, one technique is to replace characters through Linux Environment Variables. Linux Environment Variables are a set of dynamic named values, stored within the system that is used by applications launched in shells or subshells.
The printenv command prints all the environment variables in Linux:
For replacing the slash character we can use the PATH environment variable in Linux.
By adding: 0:1 we indicate to start at the character 0 and take a string of length 1.
This will produce the slash(/):
${PATH:0:1}
We can confirm it by running it with the echo command.
We can also use the LS_COLORS variable to replace the semicolon(;) operator:
Trending: OSINT Tool: Blackbird
Windows
The same concept applies to Windows. Using the cmd, we can use the Windows variable %HOMEPATH% to produce the backslash(\) character.
echo %HOMEPATH%\Users\<name_of_user>
To produce the \ character we should first specify the starting position (~ 6, which is 6 characters after the first \, => \Users), and depending on the user name length, we should specify a negative end position, in this case, is -5.
So the character inserted in the payload should be:
%HOMEPATH:~6,-5%
Bypassing Blacklisted Commands
There are some different methods when it comes to bypassing blacklisted commands. A commands blacklist is usually a list of words that are filtered out of the user input.
A command blacklist filter in PHP:
This is a basic blacklist for commands, that looks for an exact match of the provided commands.
Bypassing blacklisted commands on Linux and Windows
- Using an obfuscation technique by inserting single(‘) or double quotes (“).
The simple and easy way that works on both Linux and Windows servers is using single quotes to obfuscate a command. To obfuscate a command using quotes is simple, just add single quotes between the command’s characters:
g're'p
The important thing to remember is that you cannot mix single or double quotes, and the number of quotes must be even.
- Using Case manipulation
Another command obfuscation technique is case manipulation, which includes inverting the character cases of a command (dir ➡ DIR) or alternating between cases(dir ➡ DIR), this works only on Windows since the commands are case-insensitive and not on Linux because the commands are case-sensitive.
On Linux, we can use the tr command which is a command line utility on UNIX for translating or deleting characters.
tr "[A-Z]" "[a-z]"<<<"GreP"
This command uses tr to replace all uppercase characters with lower-case characters, which results in an all lower-case command.
Since the command contains spaces we can utilize the technique shown before for bypassing filters for blacklisted spaces, which results in the command below:
tr{IFS}"[A-Z]"{IFS}"[a-z]"<<<"GreP"
Linux Only
Another way to bypass filtered commands for Linux is using backlash (\) or the positional parameter character $@. This works like the technique with the quotes, but the number of characters does not have to be even.
g\r\e\p
gre$@p
Windows Only
This way is the same as the Linux-only characters used to bypass filtered commands, but with the caret character (^).
whoam^i
OS command Injection example lab
Solving Portswigger’s Lab: OS command injection, simple case.
- This lab contains an OS command injection vulnerability in the product stock checker.
- The application executes a shell command containing user-supplied input and returns the response of the command’s raw output.
- The goal is to execute the whoami command
Access the lab.
Since we know that the injection vulnerability lies on the product stock checker, we should then navigate to a product and intercept the POST request (the one that checks the availability of the stock).
POST Request in Burp:
The POST request contains two parameters, productid, and storeID.
Let’s try and inject an operator (ampersand, &) with an ls command at the end of the payload.
We can see that the only output is the available stock in number.
Now let’s try using a different operator (pipe, |).
The pipe (|) operator worked (semicolon also works (;), but the job can be done also with pipe) and we can now see the output of our injected command which is the stockreport.sh (the second output is only shown due to the operator used, check the cheat-sheet above).
We can then move on and play around before executing the whoami command by using cat to check the content of the script. If the script was on a real web application, just by using cat you could be able to steal data like APIs, tokens, etc., that could be in use in the script, thus making this vulnerability even worse.
Then execute the whoami command:
The user is peter-LNs5BY in our example, successfully completing the lab.
After you perform the request with the whoami command, the page updates to:
That was an easy lab without any BE, FE validation for the user input or WAF, but you get the point when it comes to injecting the commands or how to think upon finding the injection point.
Prevention
Prevention of OS command can be a whole write-up by itself but some general guidelines could be:
- Avoid using functions that execute system commands, and instead, use built-in functions that perform the required functionality as most languages have a secure implementation of these functionalities.
- Then you should still use input validation for built-in functions or system command execution functions. Input validation is required because ensures that the input meets the expected format. Input validation should be done both, at the front-end and the back-end.
- Last but more important is input sanitization, which removes unnecessary special characters from the user input. Most languages have built-in functions for removing any special characters from the user input.
Only a primer
Since Injections ranked 3rd on the OWASP Top Ten most critical web application security risks in 2021, it should be of very high importance for every developer to mitigate the risks involving OS command injection vulnerabilities, and for a security researcher to expand their knowledge on the matter.
As many web applications have millions of lines of code, any single mistake may introduce a vulnerability, by implementing secure coding best practices and then thorough penetration testing we can be assured that the risks of such vulnerabilities are mitigated to the minimum.
There are a lot more to be discussed for advanced OS command injections but we hope you enjoy this primer.
We hope that this write up has taught you something new. If you enjoyed it, the best way that you can support us is to share it! If you’d like to hear more about us, you can find us on LinkedIn, Twitter, YouTube.
Are you a security researcher? Or a company that writes articles about Cyber Security, Offensive Security (related to Information Security in general) that match with our specific audience and is worth sharing? If you want to express your idea in an article contact us here for a quote: [email protected]