当前位置: 首页 > 图文教程 > 网络编程 > PHP > php escapeshellcmd多字节编码漏洞

PHP
php 多线程上下文中安全写文件实现代码
PHP类的使用 实例代码讲解
用php实现让页面只能被百度gogole蜘蛛访问的方法
php 学习笔记
PHP编程过程中需要了解的this,self,parent的区别
php 操作excel文件的方法小结
使用PHP获取网络文件的实现代码
PHP 巧用数组降低程序的时间复杂度
php下将XML转换为数组
php 文件上传代码(限制jpg文件)
php 无极分类(递归)实现代码
PHP 采集获取指定网址的内容
PHP 将图片按创建时间进行分类存储的实现代码
PHP 存储文本换行实现方法
PHP 批量更新网页内容实现代码
用PHP查询搜索引擎排名位置的代码
用php实现的获取网页中的图片并保存到本地的代码
php实现首页链接查询 友情链接检查的代码
处理php自动反斜杠的函数代码
php实现的遍历文件夹下所有文件,编辑删除

PHP 中的 php escapeshellcmd多字节编码漏洞


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-23   浏览: 104 ::
收藏到网摘: n/a

漏洞公告在http://www.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
Escapeallcharsthatcouldpossiblybeusedto
breakoutofashellcommand
Thisfunctionemalloc'sastringandreturnsthepointer.
Remembertoefreeitwhendonewithit.
*NOT*safeforbinarystrings
*/
char*php_escape_shell_cmd(char*str){
registerintx,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''':
#ifndefPHP_WIN32
if(!p&&(p=memchr(str+x+1,str[x],l-x-1))){
/*noop*/
}elseif(p&&*p==str[x]){
p=NULL;
}else{
cmd[y++]='';
}
cmd[y++]=str[x];
break;
#endif
case'#':/*Thisischaracter-setindependent*/
case'&':
case';':
case'`':
case'|':
case'*':
case'?':
case'~':
case'<':
case'>':
case'^':
case'(':
case')':
case'[':
case']':
case'{':
case'}':
case'$':
case'':
case'x0A':/*excludingthesetwo*/
case'xFF':
#ifdefPHP_WIN32
/*sinceWindowsdoesnotallowustoescapethesechars,justremovethem*/
case'%':
cmd[y++]='';
break;
#endif
cmd[y++]='';
/*fall-through*/
default:
cmd[y++]=str[x];
}
}
cmd[y]='';
returncmd;
}
/*}}}*/

可以看到,php通过将",',#,&,;.....等等在shell命令行里有特殊意义的字符都通过在前面加上变成".',#,&,;......来进行转义,使得用户的输入被过滤,来避免产生command injection漏洞。在php看来,只要过滤了这些字符,送入到system等函数中时,参数就会是安全的,php手册中给出的利用例子如下:

<?php
$e=escapeshellcmd($userinput);
//herewedon'tcareif$ehasspaces
system("echo$e");
$f=escapeshellcmd($filename);
//andherewedo,soweusequotes
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是一个很安全的字符集,其系列中包含有对自身的校验,所以不会出现错误,会工作良好。一些系统支持多字节字符集如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@Loveshelltmp]$echo?lt;br />>
?
[loveshell@Loveshelltmp]$set|grep-ilang
LANG=zh_CN.GB2312
LANGVAR=en_US.UTF-8
[loveshell@Loveshelltmp]$exportLANG=zh_CN.GBK
[loveshell@Loveshelltmp]$echo?lt;br />?lt;br />[loveshell@Loveshelltmp]$set|grep-ilang
LANG=zh_CN.GBK
LANGVAR=en_US.UTF-8
[loveshell@Loveshelltmp]$

其中康谋嗦胛?xbf5c,可以看到在不设置LANG为GBK的时候渴且桓龇欠ǖ膅b2312编码,所以会被认为是两个字符,所以其中含有的0x5c起作用,被认为命令没结束。然后我们设置编码为GBK,烤突岜蝗衔