Command injection vulnerabilities allow an attacker to inject arbitrary system commands into an application. The commands execute at the same privilege level as the Java application and provides an attacker with functionality similar to a system shell. In Java, Runtime.exec or the ProcessBuilder are often used to invoke a new process, but they do not invoke a new command shell, which means that chaining or piping multiple commands together does not usually work. Command injection is still possible if the process spawned with Runtime.exec/ProcessBuilder is a command shell like command.com, cmd.exe, or /bin/sh.
The code below invokes the system shell in order to execute a non-executable command using user input as parameters. Non-executable Window's commands such as dir and copy are part of the command interpreter and therefore cannot be directly invoked by Runtime.exec/ProcessBuilder. The same applies to script files like batches and shell scripts. In those cases, command injections are possible and an attacker could chain multiple commands together. For example, inputting ". & rmdir /s /q C:\SomeDir" will cause the dir command to list the contents of the current directory and than quietly removes C:\SomeDir and all of its subdirectories and files.
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", "dir", args[0]);
Process process = pb.start();
To close this last gap ESAPI provides the Executor class which encodes all arguments before passing them to the command shell. Alternatively you could use the encodeForOS() method from the encoder to encode the arguments on your own and than pass them to Runtime.exec/ProcessBuilder. If you are looking for the output of a command shell you should use the Executor because it also handles the concurrent output and error stream and provides them as String results.
List<String> args = new ArrayList<String>();
args.add("/C");
args.add("dir");
args.add(arg[0]);
ExecuteResult er = ESAPI.executor().executeSystemCommand(
new File("C:\\Windows\\System32\\cmd.exe"), args);
String output = er.getOutput();
String errors = er.getErrors();
You should avoid invoking the shell using Runtime.exec/ProcessBuilder in order to call operating system specific commands and should use Java APIs instead. For example, instead of calling ls or dir from the shell use the Java File class and the list function.