前几天提到CVE-2012-1172这个漏洞,今天来把分析过程写下。
其实PHP在处理文件上传的时候,曾经发生过多个类似的漏洞,我们先列举几个:
2004年:PHP处理RFC1867 MIME格式导致数组错误漏洞
http://www.nsfocus.net/index.php?act=ma … w&mid=2368
2009年:PHP Bug #49683:$_FILES overwrite
https://bugs.php.net/bug.php?id=49683
2011年:File path injection in PHP 5.3.6 file upload (CVE 2011-2202)
http://blog.kotowicz.net/2011/06/file-p … -file.html
CVE-2012-1172实际上和上面的漏洞比较相似,我们先看poc
我们同时上传一个jpg文件和一个txt文件,返回结果如下:
Array ( [file] => Array ( [name] => Array ( [[type] => text/plain [[name] => test.txt [[tmp_name] => C:\Windows\Temp\phpAFF2.tmp [[error] => 0 [[size] => 5231 ) [type] => Array ( [[type] => image/jpeg ) [tmp_name] => Array ( [[type] => C:\Windows\Temp\phpAFE1.tmp ) [error] => Array ( [[type] => 0 ) [size] => Array ( [[type] => 5145 )
明显的处理异常发生了变量覆盖的情况,因为如果是正常的文件上传,应该是返回类似如下结果的数组
Array ( [file] => Array ( [name] => Array ( [type] => 11.jpg [name] => test.txt ) [type] => Array ( [type] => image/jpeg [name] => text/plain ) [tmp_name] => Array ( [type] => C:\Windows\Temp\phpD4C7.tmp [name] => C:\Windows\Temp\phpD4C8.tmp ) [error] => Array ( [type] => 0 [name] => 0 ) [size] => Array ( [type] => 5145 [name] => 5231 ) ) )
下面我们来分析下漏洞是怎么产生的。查看文件 rfc1867.c中对于是否为合法上传的判断:
992 if (!skip_upload) { 993 char *tmp = param; 994 long c = 0; 995 996 while (*tmp) { 997 if (*tmp == ‘[') { 998 c++; 999 } else if (*tmp == ']‘) { 1000 c–; 1001 if (tmp[1] && tmp[1] != ‘[') { 1002 skip_upload = 1; 1003 break; 1004 } 1005 } 1006 if (c < 0) { 1007 skip_upload = 1; 1008 break; 1009 } 1010 tmp++; 1011 } 1012 }
这地方没有考虑到c>0的可能,也就是说我们提交的变量名称中允许出现[比]数量多的情况,比如可以为file[[type]或者file[name][的形式。正是由于这两种格式的变量处理不当才导致了变量覆盖的问题。继续看代码:
1146 /* is_arr_upload is true when name of file upload field 1147 * ends in [.*] 1148 * start_arr is set to point to 1st [ */ 1149 is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ‘]’);
这个地方判断是否为批量上传的比较关键,需要以]结尾的才认为是批量上传,具体来说file[[type]可以使is_arr_upload为1而 file[name][则使is_arr_upload为0,之后处理的方式完全不同
1151 if (is_arr_upload) { 1152 array_len = strlen(start_arr); 1153 if (array_index) { 1154 efree(array_index); 1155 } 1156 array_index = estrndup(start_arr + 1, array_len - 2); 1157 } …… 1225 /* Add $foo[name] */ 1226 if (is_arr_upload) { 1227 snprintf(lbuf, llen, “%s[name][%s]“, abuf, array_index); 1228 } else { 1229 snprintf(lbuf, llen, “%s[name]“, param); 1230 } …… 1260 /* Add $foo[type] */ 1261 if (is_arr_upload) { 1262 snprintf(lbuf, llen, “%s[type][%s]“, abuf, array_index); 1263 } else { 1264 snprintf(lbuf, llen, “%s[type]“, param); 1265 }
file[[type]按照is_arr_upload为1的方式处理,产生的变量依次为file[name][[type]和
file[type][[type]。
file[name][按照is_arr_upload为0的方式处理,产生的变量依次为 file[name][[name]和
file[name][[type]。
按照注册的顺序,后者会覆盖前者产生的相同的变量。
漏洞分析就到这里,具体在实战中的运用还要看应用的代码,在一定条件下是可以下载文件或者上传非法文件的。
官方修复的方式也比较简单
— main/rfc1867.c 2012/01/01 23:51:21 321663 +++ main/rfc1867.c 2012/01/01 23:54:25 321664 @@ -942,6 +942,10 @@ } tmp++; } + /* Brackets should always be closed */ + if(c != 0) { + skip_upload = 1; + } }
参考:
https://nealpoole.com/blog/2011/10/dire … e-uploads/
http://lxr.php.net/opengrok/xref/PHP_5_2/main/rfc1867.c
转自:http://bbs.wolvez.org/viewtopic.php?id=267