PHP Multibyte Shell Command Escaping Bypass Vulnerability

May 7, 2008 – 4:41 AM

In PHP there exist two functions to escape shell commands or arguments to shell commands that are used in PHP applications to protect against shell command injection vulnerabilities.
– escapeshellcmd()
– escapeshellarg()

Unfortunately it was discovered that both functions fail to protect against shell command injection when the shell uses a locale with a variable width character set like GBK, EUC-KR, SJIS, ..

This can lead to arbitrary shell command injection vulnerabilities in PHP applications believed to be safe. In addition to that exploiting this problem in PHP functions that use this shell escaping internally allows safe_mode and disable_functions bypass.

Details:
escapeshellcmd()
escapeshellcmd() will put a single backslash character in front of every shell meta character like ; $ < > … to escape it. This function is normally used to ensure that only a single shell command is executed and that it is not possible to append further shell commands.

The problem is that the backslash character is a legal second byte of several variable width encodings. Because of this a shell that is for example configured to use a locale with the GBK character set will consider the introduced backslash as part of a multibyte character instead of an escaping of following meta character.

Example:
escapeshellcmd(“echo “.chr(0xc0).”;id”);

Executing the result of this will therefore result in echo and id being executed.

escapeshellarg()
escapeshellarg() does not use the backslash character to escape shell meta characters. Instead it places the argument in single quotes and only escapes single quotes in the qrgument with the string ‘\” . Because of this it is not possible to use the same trick. However in case there are multiple inputs it is possible to “eat” the terminating single quote which results in a shell command injection through the second argument.

Example:
$arg1 = chr(0xc0);
$arg2 = “; id ; #”;
$cmd = “echo “.escapeshellarg($arg1).” “.escapeshellarg($arg2);

In this example the 0xC0 character forms a multibyte character with the terminating single quote. Therefore the starting single quote of $arg2 will be used as terminating single quote and the content of $arg2 can be used to inject everything.

NOTE: This attack works because even invalid second byte characters are accepted on several platforms as valid.

safe_mode_exec_dir bypass
Because of the vulnerability described above, it is possible to bypass the safe_mode_exec_dir directive of PHP. This directive is supposed to ensure that only shell commands within the allowed directory can be executed.

This attack is however only feasible when the shell uses one of the vulnerable locales, because during safe_mode it is not possible to set the LANG environment variable that would influence the shell.

mail() fifth parameter – disable_functions bypass
Because of the vulnerability described above, it is possible to execute arbitrary shell commands on a system even when all shell execution functions like shell_exec(), system(), … are disabled by the disable_functions directive, but mail() is still allowed. This attack relies on the fact that the fifth mail() parameter is used as argument to the sendmail binary and escaped with escapeshellcmd() internally to ensure that no further shell commands are appended.

Because PHP scripts can influence the locale of the shell (unless running in safe_mode) this attack allows bypassing the setting of disable_functions when a vulnerable locale is installed on the system. In case the system’s shell does not support one of the vulnerable character sets the attack is not feasible.

Read the rest of the story…

You must be logged in to post a comment.