From: Rui Hirokawa Date: Thu, 10 Nov 2011 14:19:06 +0000 (+0000) Subject: MFH: fixed bug #60116 (escapeshellcmd() cannot escape the characters which cause... X-Git-Tag: php-5.4.0RC2~108 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=db5d9c0b6693ce29278d0b58cb30a55c48f84d4f;p=php MFH: fixed bug #60116 (escapeshellcmd() cannot escape the characters which cause shell command injection). --- diff --git a/NEWS b/NEWS index ba61ca1136..2cb7a5659a 100644 --- a/NEWS +++ b/NEWS @@ -24,8 +24,10 @@ PHP NEWS . Fixed bug #60169 (Conjunction of ternary and list crashes PHP). (Laruence) . Fixed bug #55475 (is_a() triggers autoloader, new optional 3rd argument to - is_a and is_subclass_of). (alan_k) - + is_a and is_subclass_of). (alan_k) + . Fixed bug #60116 (escapeshellcmd() cannot escape the characters + which cause shell command injection). (rui) + - Oracle Database extension (OCI8): . Increased maxium Oracle error message buffer length for new 11.2.0.3 size (Chris Jones) diff --git a/ext/standard/exec.c b/ext/standard/exec.c index ac96fe6815..3b6d41167d 100644 --- a/ext/standard/exec.c +++ b/ext/standard/exec.c @@ -50,6 +50,16 @@ #include #endif +/* {{{ register_exec_constants + * */ +void register_exec_constants(INIT_FUNC_ARGS) +{ + REGISTER_LONG_CONSTANT("ESCAPE_CMD_PAIR", ESCAPE_CMD_PAIR, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ESCAPE_CMD_END", ESCAPE_CMD_END, CONST_PERSISTENT|CONST_CS); + REGISTER_LONG_CONSTANT("ESCAPE_CMD_ALL", ESCAPE_CMD_ALL, CONST_PERSISTENT|CONST_CS); +} +/* }}} */ + /* {{{ php_exec * If type==0, only last line of output is returned (exec) * If type==1, all lines will be printed and last lined returned (system) @@ -238,7 +248,7 @@ PHP_FUNCTION(passthru) *NOT* safe for binary strings */ -PHPAPI char *php_escape_shell_cmd(char *str) +PHPAPI char *php_escape_shell_cmd_ex(char *str, int flag) { register int x, y, l = strlen(str); char *cmd; @@ -266,14 +276,26 @@ PHPAPI char *php_escape_shell_cmd(char *str) #ifndef PHP_WIN32 case '"': case '\'': - if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) { - /* noop */ - } else if (p && *p == str[x]) { - p = NULL; - } else { + if (flag == ESCAPE_CMD_ALL) { cmd[y++] = '\\'; + cmd[y++] = str[x]; + } else if (flag == ESCAPE_CMD_END) { + if ((x == 0 || x == l - 1) && (str[0] == str[l-1])) { + cmd[y++] = str[x]; + } else { + cmd[y++] = '\\'; + cmd[y++] = str[x]; + } + } else { /* ESCAPE_CMD_PAIR */ + if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) { + /* noop */ + } else if (p && *p == str[x]) { + p = NULL; + } else { + cmd[y++] = '\\'; + } + cmd[y++] = str[x]; } - cmd[y++] = str[x]; break; #else /* % is Windows specific for enviromental variables, ^%PATH% will @@ -327,6 +349,14 @@ PHPAPI char *php_escape_shell_cmd(char *str) } /* }}} */ +/* {{{ php_escape_shell_cmd + */ +PHPAPI char *php_escape_shell_cmd(char *str) +{ + return php_escape_shell_cmd_ex(str, ESCAPE_CMD_PAIR); +} +/* }}} */ + /* {{{ php_escape_shell_arg */ PHPAPI char *php_escape_shell_arg(char *str) @@ -397,14 +427,15 @@ PHP_FUNCTION(escapeshellcmd) { char *command; int command_len; + long flag = ESCAPE_CMD_PAIR; char *cmd = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &command, &command_len, &flag) == FAILURE) { return; } if (command_len) { - cmd = php_escape_shell_cmd(command); + cmd = php_escape_shell_cmd_ex(command, flag); RETVAL_STRING(cmd, 0); } else { RETVAL_EMPTY_STRING(); diff --git a/ext/standard/tests/general_functions/bug60116.phpt b/ext/standard/tests/general_functions/bug60116.phpt new file mode 100644 index 0000000000..deade1e0fd --- /dev/null +++ b/ext/standard/tests/general_functions/bug60116.phpt @@ -0,0 +1,160 @@ +--TEST-- +Test escapeshellcmd() to escape the quotation +--SKIPIF-- + +--FILE-- +', + '()[]{}$', + '%^', + '#&;`|*?', + '~<>\\', + '%NOENV%', + "abc' 'def", + 'abc" "def', + "'abc def'", + '"abc def"', +); + +echo "case: default\n"; + +$count = 1; +foreach ($data AS $value) { + echo "-- Test " . $count++ . " --\n"; + var_dump(escapeshellcmd($value)); +} + +echo "case: ESCAPE_CMD_PAIR\n"; +$count = 1; +foreach ($data AS $value) { + echo "-- Test " . $count++ . " --\n"; + var_dump(escapeshellcmd($value, ESCAPE_CMD_PAIR)); +} + +echo "case: ESCAPE_CMD_END\n"; +$count = 1; +foreach ($data AS $value) { + echo "-- Test " . $count++ . " --\n"; + var_dump(escapeshellcmd($value, ESCAPE_CMD_END)); +} + +echo "case: ESCAPE_CMD_ALL\n"; +$count = 1; +foreach ($data AS $value) { + echo "-- Test " . $count++ . " --\n"; + var_dump(escapeshellcmd($value, ESCAPE_CMD_ALL)); +} + +echo "Done\n"; +?> +--EXPECTF-- +*** Testing escapeshellcmd() escape the quotation *** +case: default +-- Test 1 -- +string(5) "\"abc" +-- Test 2 -- +string(5) "\'abc" +-- Test 3 -- +string(6) "\?\<\>" +-- Test 4 -- +string(14) "\(\)\[\]\{\}\$" +-- Test 5 -- +string(3) "%\^" +-- Test 6 -- +string(14) "\#\&\;\`\|\*\?" +-- Test 7 -- +string(8) "\~\<\>\\" +-- Test 8 -- +string(7) "%NOENV%" +-- Test 9 -- +string(9) "abc' 'def" +-- Test 10 -- +string(9) "abc" "def" +-- Test 11 -- +string(9) "'abc def'" +-- Test 12 -- +string(9) ""abc def"" +case: ESCAPE_CMD_PAIR +-- Test 1 -- +string(5) "\"abc" +-- Test 2 -- +string(5) "\'abc" +-- Test 3 -- +string(6) "\?\<\>" +-- Test 4 -- +string(14) "\(\)\[\]\{\}\$" +-- Test 5 -- +string(3) "%\^" +-- Test 6 -- +string(14) "\#\&\;\`\|\*\?" +-- Test 7 -- +string(8) "\~\<\>\\" +-- Test 8 -- +string(7) "%NOENV%" +-- Test 9 -- +string(9) "abc' 'def" +-- Test 10 -- +string(9) "abc" "def" +-- Test 11 -- +string(9) "'abc def'" +-- Test 12 -- +string(9) ""abc def"" +case: ESCAPE_CMD_END +-- Test 1 -- +string(5) "\"abc" +-- Test 2 -- +string(5) "\'abc" +-- Test 3 -- +string(6) "\?\<\>" +-- Test 4 -- +string(14) "\(\)\[\]\{\}\$" +-- Test 5 -- +string(3) "%\^" +-- Test 6 -- +string(14) "\#\&\;\`\|\*\?" +-- Test 7 -- +string(8) "\~\<\>\\" +-- Test 8 -- +string(7) "%NOENV%" +-- Test 9 -- +string(11) "abc\' \'def" +-- Test 10 -- +string(11) "abc\" \"def" +-- Test 11 -- +string(9) "'abc def'" +-- Test 12 -- +string(9) ""abc def"" +case: ESCAPE_CMD_ALL +-- Test 1 -- +string(5) "\"abc" +-- Test 2 -- +string(5) "\'abc" +-- Test 3 -- +string(6) "\?\<\>" +-- Test 4 -- +string(14) "\(\)\[\]\{\}\$" +-- Test 5 -- +string(3) "%\^" +-- Test 6 -- +string(14) "\#\&\;\`\|\*\?" +-- Test 7 -- +string(8) "\~\<\>\\" +-- Test 8 -- +string(7) "%NOENV%" +-- Test 9 -- +string(11) "abc\' \'def" +-- Test 10 -- +string(11) "abc\" \"def" +-- Test 11 -- +string(11) "\'abc def\'" +-- Test 12 -- +string(11) "\"abc def\"" +Done