Good Habits Part II-a: Safe Command Execution
If you've been even moderately exposed to application security, particularly web application security, you should see the title and immediately think I've lost my mind. With output filtering, since there are so very many examples of how to do the more common things (HTML encoding, SQL Prepared statements, etc.), I thought with the output filtering examples, I'd try to do a couple of twists on each of those. So the examples I give will not be typical.
That being said, the absolute first recommendation with command execution is don't do it! At all. If you can possibly avoid it. Do anything you can to not have to do command execution.
Now that that is cleared, there is a safe (well, safer) way to deal with command execution than trying to deal with shell metacharacters yourself. Almost all environments and frameworks have a form of a multi-argument command execution function. In *nix environments, they're all based on the execve(2) command and its descendants (execl(3), execlp(3), execle(3), execv(3), execvp(3), and execvP(3)). Here's how you deal with this in some of the more common environments:
C/C++
execve(2) and descendants replace the current command with the one specified. So if you only want the command executed and need to do something afterwards, you must fork(2) first. Dealing with input and output is left as an exercise for the reader. In P-code, it looks something like this:
pid = fork();Now you probably don't want to spend a lot of time parsing environment and such in a C CGI, but the example is there because it applies to other environments, such as...
if (pid) {
/* This is the parent process.
wait for junior to finish up. */
wpid = wait(pid, &status, flags);
} else {
/* I'm the child process.
call my nasty command-line */
execv(cmd, args);
}
Perl
P-code looks exactly the same. But I need to mention that the usual way of doing execution in Perl is to use backticks. DO NOT DO THIS! You can't possibly do enough input validation to know that every metacharacter will be taken care of. And even if you get it right, you're going to make mistakes in the future. Also, don't do system() which would seem to be a good way to keep from replacing this process - it behaves the same as the backticks, so metacharacters aren't properly escaped. Do it just like you do in C/C++ - fork, exec, wait.
Ruby
The scripting languages closely tied to the system are nice because I don't have to spell out new examples. With Ruby, use the C/C++ means. It's harder, but that's what you get for thinking you need command execution. Fortunately, because of closures, it's a little prettier in Ruby:
fork {
exec(cmd, args)
}
Process.wait
PHP
Not that I'm lazy (okay, I am) but pcntl functions actually have to be enabled at compile time. I don't use PHP much, so the PHP I have here wasn't compiled with it enabled, and I won't be compiling another anytime soon to enable it. The functions work just like their C parents, so the model will look like the others:
$pid = pcntl_fork();With all of the C derivatives, there's a lot more error checking you could and should do. It would behoove you to find out about zombie processes, reaping dead children, and the like. And again - if you insist on doing command execution, you asked for it.
if ($pid) {
# In the parent
} else {
pcntl_exec($cmd, $args);
}
Java
Java's version of "safely" starting a command is a little more elegant (IMO) than the C derivatives. In Java, it's much easier to get STDIN, STDOUT, and STDERR handles for the newly-created process. If you have a pre-1.5 Java, you can use Runtime.exec(String[]), and if you have 1.5 or newer, you can use the new ProcessBuilder, whose two contructors are a List<String> version and a String... varargs version. Runtime.exec(String) does not escape shell metacharacters, so stay away from it.
When you call Runtime.exec() or ProcessBuilder.start(), you'll be returned a Process object that you can use to wait for the process to complete, and it also has input and output handles to get results from the process, as well as the return code.
.NET
Sadly, from the reading I've done on fixing this in .NET, there's not a really good solution. There's a nice OO interface as in Java - System.Diagnostics.Process, but Process has an Arguments property that is a single string with all the arguments, so it'll be processed by the shell with all its metacharacters. So you're going to end up being stuck with the Windows API for starting commands to work around those, which is even uglier than the fork, exec, wait set on *nix'es. If anybody has a more elegant solution that includes metacharacter filtering on .NET, please let me know.
Conclusion
Now, you still have lots of other things to consider - has the command you want to call been replaced by some other? How does environment play into all of this? And the most important thing to consider is whose effective ID will be running this process. Just like any other form of injection, you still have to consider the rights of the process that starts this off.
And let me reiterate - don't do this at all unless it's absolutely, positively necessary and there is no other means to accomplish what it is you want to do.
0 comments:
Post a Comment