Link Search Menu Expand Document

OS Command Injection in Java

Play SecureFlag Play Java Labs on this vulnerability with SecureFlag!

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