OS Command Injection in Java
Vulnerable example
The following Java method invokes Runtime.exec()
, which receives unsanitized data originating from the environment, making this code susceptible to a command injection attack.
public static void listFiles(String dir) throws Exception {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(new String[] {"sh", "-c", "ls " + dir});
int result = proc.waitFor();
if (result != 0) {
System.out.println("process error: " + result);
}
InputStream in = (result == 0) ? proc.getInputStream() :
proc.getErrorStream();
int c;
while ((c = in.read()) != -1) {
System.out.print((char) c);
}
}
Prevention
Solution #1 (Allow list) This solution sanitizes the untrusted user input by permitting only a small group of allowed characters in the argument that will be passed to Runtime.exec()
; all other characters are excluded.
// ...
if (!Pattern.matches("[0-9A-Za-z@.]+", dir)) {
// Handle error
}
// ...
Solution #2 (Restricted User Choice) This solution prevents command injection by passing only trusted strings to Runtime.exec()
. The user has control over which string is used but cannot provide string data directly to Runtime.exec()
.
// ...
String dir = null;
int number = Integer.parseInt(System.getProperty("dir")); // Only allow integer choices
switch (number) {
case 1:
dir = "data1";
break; // Option 1
case 2:
dir = "data2";
break; // Option 2
default: // Invalid
break;
}
if (dir == null) {
// Handle error
}
This solution can quickly become unmanageable if you have many available directories. A more scalable solution is to read all the permitted directories from a properties file into a java.util.Properties
object.
Solution #3 (Avoid Runtime.exec()
) When the task performed by executing a system command can be accomplished by some other means, it is almost always advisable to do so. This compliant solution uses the File.list()
method to provide a directory listing, eliminating the possibility of command or argument injection attacks.
import java.io.File;
public static void listFiles(String[] args) throws Exception {
File dir = new File(System.getProperty("dir"));
if (!dir.isDirectory()) {
System.out.println("Not a directory");
} else {
for (String file : dir.list()) {
System.out.println(file);
}
}
}
References
Mitre - CWE-78: Improper Neutralization of Special Elements used in an OS Command OWASP - Command Injection IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method