php escapeshellcmd 多字节编码漏洞解析及延伸
创建时间:2008-05-07 文章属性:
原创 文章提交:T_Torchidy (jnchaha_at_163)
漏洞公告在 sektioneins.de/advisories/SE-2008-03.txt
PHP 5 <= 5.2.5 PHP 4 <= 4.4.8
一些允许如 GBK,EUC-KR, SJIS 等宽字节字符集的系统都可能受此影响,影响还是非常大的,国内的 虚拟主机应该是通杀的,在测试完这个漏洞之后,发现还是十分有意思的,以前也有过对这种类型安全漏 洞的研究,于是就把相关的漏洞解释和一些自己的想法都写出来,也希望国内的一些有漏洞的平台能迅速 做出响应,修补漏洞。 这个漏洞出在 php 的用来转义命令行字符串的函数上, 这些函数底层是用的 php_escape_shell_cmd 这 个函数的,我们先来看看他的处理过程:
/* {{{ php_escape_shell_cmd Escape all chars that could possibly be used to break out of a shell command
This function emalloc's a string and returns the pointer. Remember to efree it when done with it.
*NOT* safe for binary strings */ char *php_escape_shell_cmd(char *str) { register int x, y, l; char *cmd; char *p = NULL;
l = strlen(str); cmd = safe_emalloc(2, l, 1);
for (x = 0, y = 0; x < l; x++) { switch (str[x]) { case '"': case '\'': #ifndef
PHP_WIN32 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]; break; #endif case '#': /* This is character-set independent */ case '&;': case ';': case '`': case '|': case '*': case '?': case '~': case '<': case '>': case '^': case '(': case ')': case '[': case ']': case '{': case '}': case '$': case '\\': case '\x0A': /* excluding these two */ case '\xFF': #ifdef PHP_WIN32 /* since Windows does not allow us to escape these chars, just remove them */ case '%': cmd[y++] = ' '; break; #endif cmd[y++] = '\\'; /* fall-through */ default: cmd[y++] = str[x];
} } cmd[y] = '\0'; return cmd;
} /* }}} */
可以看到,php 通过将",',#,&;,;.....等等在 shell 命令行里有特殊意义的字符都通过在前面加上\变 成\".\',\#,\&;,\;......来进行转义,使得用户的输入被过滤,来避免产生 command injection 漏洞。在 php 看来,只要过滤了这些字符,送入到 system 等函数中时,参数就会是安全的,php 手册中给出的利用 例子如下:
// here we don't care if $e has spaces system("echo $e"); $f = escapeshellcmd($filename);
// and here we do, so we use quotes system("touch \"/tmp/$f\"; ls -l \"/tmp/$f""); ?>
很明显,如果没有经过 escapeshellcmd 的处理,用户输入 hello;id 的话,最后 system 执行的会是:
echo hello;id
;在 shell 里是分割命令的作用,这样不仅仅会 echo hello,还会执行 id 这个命令,导致命令注入漏洞。 用 escapeshellcmd 处理之后命令变成:
echo hello\;id
这样执行的命
令就只会是 echo,其他的都变成 echo 的参数,很安全。
事实上是这样么?php 在处理完参数送入 system 之后它就什么都不管了,后面的工作实际上都是由 linux 来完成的, 那么 linux 在处理这些参数的时候是怎么样的呢?linux 在执行命令的时候会有一些的表 示工作环境的环境变量,譬如 PWD 代表当前的
工作环境,UID 代表了你的身份,BASH 代表命令解释器等 等......而在 linux 系统执行命令的时候,还有一个非常重要的参数,LANG,这个参数决定了 linux shell 如何处理你的输入,这样就可以当你输入一些中文字符的时候,
linux 能认识他,不至于出现人与系统之 间出现理解上的错误。默认情况下,linux 的 LANG 是 en_US.UTF-8,UTF-8 是一个很安全的字符集,其系 列中包含有对自身的校验,所以不会出现错误,会工作良