Injections are one of the most common vulnerabilities in applications. Depending on what environment and utilities you use, there can be a variety of injection flaws . Among these types, command injection is one of the most dangerous. The impact of command injection can range from stealing data, changing system configurations, or even bringing the whole system down. Malicious actors sometimes use command injection to create security weaknesses in the system and then exploit the newly created weaknesses.
Itโs crucial to secure applications from vulnerabilities. And Java is one of the most popular programming languages for application development. So, in this post, letโs see what command injection is and how it works in Java and, finally, understand how we can prevent command injection vulnerabilities.
What Is Command Injection?
Command injection is a technique where a malicious actor tries to execute OS commands on the system hosting the application. Some features might need you to use system commands. And in some cases, user input is part of these commands. For example, you could be storing usersโ data through your application in a folder named after the username. You could be using system commands to check if a userโs directory exists and then show the files in that directory.
In such cases, youโre using some information provided by the user (the username) and using it in system commands. This creates a possibility for command injection vulnerabilities. For a command injection attack to work, the application should meet three main conditions:
The application should have privileges/permissions to execute system commands.
The application should use user-provided data as a part of system commands.
The user-provided data should not be escaped/sanitized before use.
If your application meets these conditions, then thereโs a pretty good chance that your application is vulnerable to command injections. You canโt always trust user input. So, letโs take the above example where youโre storing user data in a folder. If the user enters a valid username, then itโs all good. The application executes the command and everything goes as planned. But if a malicious user gives an injection as the input, then thereโs a problem.
To understand command injection better, letโs take an example and see how it works.
How Does Command Injection Work?
Hereโs a Java program that meets all three conditions mentioned above:
import java.util.*;
import java.io.File;
import java.io.*;
public class command_injection{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
System.out.println("Enter Username");
String user = sc.nextLine();
String user_path = ".\Data\"+user;
File file = new File(user_path);
try {
String comm = "cmd.exe /c dir "+user_path;
Process process = Runtime.getRuntime().exec(comm);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
}
catch (IOException e) {
System.out.println("Error executing command");
}
}
}
The above code asks the user for a username. When the user enters the username, the code checks if thereโs a folder that matches the username. If yes, it uses a system command to display the contents of the folder; if not, it displays an error message. Iโve created a folder named โTonyโ and created a few files in it.
First, letโs see what happens if we give valid input:
When I give โTonyโ as input, the application generates the following line:
cmd.exe /c dir ./Data/Tony
cmd.exe is the command prompt, and it executes the command dir ./Data/Tony . This shows us all the files in the folder named โTonyโ. Now, what if along with the username, we added something malicious. Letโs say I give the following input as the username:
Tony & ping -n 2 8.8.8.8
Based on this input, the application generates the following command to execute:
dir ./Data/Tony & ping -n 2 8.8.8.8
& is used to run multiple commands in a single line. When the above is executed, it first checks for a folder named โTonyโ. And because we have that folder, it will display the contents of that folder. But the command doesnโt end there. The next part of the command is pinging to an IP. Now, the application also executes this part of the command:
This is how command injections work. Malicious actors craft input such that it manipulates the original function of the application.
What if the injection was more dangerous than just pinging to an IPโlike, โshut down the systemโ or โdelete an important fileโ? The result would be catastrophic.
Dangerous Payloads
Hereโs an example of a payload for Windows to delete a folder:
Tony & dir & rmdir /Q /S Important & dir
This payload should delete the folder named Important. Iโm using the dir command to display the contents of the folder before and after deletion.
Another example is if the input is Tony & shutdown /s . The command would be:
dir ./Data/Tony & shutdown /s
If the application executes this command, the command prompt would first display the contents of the file and then shut down the system. When this command is executed on the server, the whole application could go down.
Here are some examples of dangerous commands for Windows and Linux-based operating systems. NOTE: Do not run these commands unless you know what youโre doing!
Sounds scary, doesnโt it? But donโt worry, we can protect ourselves from command injections.
Preventing Command Injection Attacks
Thereโs no master key to prevent command injections. It means that you canโt just implement one thing and expect to be secure. You need to add multiple layers of security . When it comes to security, the more, the better. So, here are some common and useful prevention methods.
If youโre not using system commands, then command injections arenโt possible.
Do You Need System Commands?
This is the first question you need to ask yourself. If youโre not using system commands, then command injections arenโt possible. So, the first step is to avoid using system commands.
The next question to ask yourself is, โAre there any alternatives?โ In some cases, you have to use system functions. Thereโs no workaround. But you donโt necessarily need to use system commands. Java offers a wide range of libraries and functions that do the same task as system commands. So, youโd still be doing the same thing but not by directly executing system commands.
Letโs take an example of the use-case discussed above. You have to check if a userโs directory exists, and if it does, show the files in that directory. The following code has two sections: first, where we use a library, and second, where weโre using direct system commands on the command line.
import java.util.*;
import java.io.File;
import java.io.*;
import java.nio.file.Files;
public class a{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
System.out.println("Enter Username");
String user = sc.nextLine();
String user_path = ".\Data\"+user;
File file = new File(user_path);
try {
System.out.println("n****** Output from library ******n");
Files.list(new File(user_path).toPath())
.limit(10)
.forEach(path -> {
System.out.println(path);
});
}
catch (IOException e) {
System.out.println("Error executing command");
}
try {
String comm = "cmd.exe /c dir "+user_path;
System.out.println("n****** Output from command line ******n");
Process process = Runtime.getRuntime().exec(comm);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
}
catch (IOException e) {
System.out.println("Error executing command");
}
}
}
When you give a valid inputโi.e., Tony. โyou get proper results from both approaches.
But when you give a malicious payload as input, the library function throws an error. Whereas the application executes the payload when using system commands.
The advantage of using such libraries over system commands is that most of these libraries have considered the security aspects and have implemented security measures and checks.
Escaping Input
In most cases, for a command injection to work, a user needs to use multiple commands in a single line. And these commands are delimited by certain characters. In the above example, we saw how the character & (ampersand) was used to run two commands. But thatโs not itโthere are more such characters . Some common characters are ; (semicolon) and | (pipeline). Thereโs also backtick ( ` ), which has a special meaning. When a command is enclosed within backticks, that command is evaluated before the main command is executed. For example, if the command looks like:
command_1 `command_2`
Then command_2 is executed first and its output is used in command_1.
You should escape such characters in user input. You could validate the input, strip these characters, or parse input with these characters to get the desired data.
Hereโs a code snippet to strip and escape these special characters:
Once I generate the command as shown in the code above, Iโll use the following code to strip and escape special characters.
String strip = comm.replaceAll("[;&|`]*","");
System.out.println("Stripped: " + strip);
String escape = comm.replaceAll("[;&|`]+","\u" + Integer.toHexString('/' | 0x10000).substring(1)); //Unicode
System.out.println("Stripped: " + escape);
Here, you can see that the code strips and escapes the special characters, making the injection useless. The part of the command to ping to 8.8.8.8 is not executed.
You can strip characters when you just want to remove the characters. You can use escaping if you want to further log and analyze inputs. When you log inputs with Unicode data, you can convert the Unicode characters back to string to see the actual input.
Principle of Least Privilege
The principle of least privilege says that you should give an entity the least amount of privilege necessaryโjust enough to do whatโs needed. For example, if the application or user needs access to just one folder, then give access to only that folder. Giving more permissions or superuser privileges is not at all smart. Implement this principle everywhere.
You could also create a separate user for the application and give only required permissions to that user. This might not be feasible everywhereโit depends on the use-caseโbut you should consider it.
Allowlist and Denylist
If you know what commands must be used, or what commands must not be used, you could allow or block them. If your application needs to execute only one command, then you could use a logic that checks if the command being sent to the command line for execution is that one command you intend to execute. Letโs say you only need to execute the command ls /home/Documents _. Y_ou donโt need to execute any other command. Then you can use a logic similar to the following:
if (command == "ls /home/Documents") {
Process process = Runtime.getRuntime().exec(command);
}
Allow-listing and deny-listing are very useful because they make most of the crafted injections useless. But to implement these, you need to have a complete idea of all the scenarios. If you donโt, it could affect the applicationโs functioning.
Manual security testing has its perks, but automated security testing makes things really easy.
Security Testing
Nobody is completely secure. Even after spending a lot of time and effort, we canโt be 100% secure. And to know where you could get better, you need security testing. Security testing is a continuous process in application security. You can find new vulnerabilities, new attacks, and new tools almost every day. And you need to keep up with malicious actors.
Manual security testing has its perks, but automated security testing makes things really easy. If youโre looking for such a product, try StackHawk . StackHawk offers automated application security testing on every pull request. It continuously finds API and application vulnerabilities and makes security analysis easy.
This post was written by Omkar Hiremath. Omkar is a cybersecurity analyst who is enthusiastic about cybersecurity, ethical hacking, data science, and Python. Heโs a part time bug bounty hunter and is keenly interested in vulnerability and malware analysis.