当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-082383

漏洞标题:kppw任意文件上传-1

相关厂商:kppw

漏洞作者: 路人甲

提交时间:2014-11-07 16:31

修复时间:2015-02-05 16:32

公开时间:2015-02-05 16:32

漏洞类型:文件上传导致任意代码执行

危害等级:中

自评Rank:10

漏洞状态:未联系到厂商或者厂商积极忽略

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-11-07: 积极联系厂商并且等待厂商认领中,细节不对外公开
2015-02-05: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

KPPW是客客团队主打的开源威客系统,同类开源建站产品的领跑者,搭建无物流电子商务在线服务交易平台的首选系统。2014年6月新版KPPW2.5发布了,新版借助客客团队近4年来威客行业的开源产品和商业项目的成熟经验,对前端功能进行了深入改进。KPPW从产品规划、UI设计、程序开发多个环节进行了优化,在未做框架重构情况下对程序进行更优秀的改进。

详细说明:

在control/ajax/upload.php中:

$pathDir = setUploadPath($fileType, $objType);
$upload = new keke_upload_class(S_ROOT.$pathDir ,$fileFormat,$maxSize);
$savename = $upload->run( $filename , 1);


再来看run方法:

function run($fileInput, $randName = 1) {
if (isset ( $_FILES [$fileInput] )) {
$fileArr = $_FILES [$fileInput];
if (is_array ( $fileArr ['name'] )) {
....
}
else {
$this->getExt ( $fileArr ['name'] );
$this->setSavename ();
if ($this->copyfile ( $fileArr, $randName )) {
$this->returnArray [] = $this->returninfo;
} else {
$this->returninfo ['error'] = $this->errmsg ();
$this->returnArray [] = $this->returninfo;
}
return $this->errno ? $this->errmsg () : $this->returnArray;
}
return false;
}


在这段方法中,先是获取了$this->getExt ( $fileArr ['name'] ); 文件后缀,这里没有什么问题,然后再生成上传后的随机名+后缀. 最后执行上传操作copyfile

function copyfile($fileArray, $randName) {
$this->returninfo = array ();
$this->returninfo ['name'] = $fileArray ['name'];
if ($randName) {
$this->returninfo ['saveName'] = $this->saveName;
} else {
$this->saveName = $this->returninfo ['saveName'] = $fileArray ['name'];
}
$this->returninfo ['size'] = $fileArray ['size'];
$this->returninfo ['type'] = $fileArray ['type'];
if (! $this->validateFormat ()) {
$this->errno = 11;
return false;
}
if(!$this->fileFilter($fileArray ["tmp_name"],$this->ext)){
$this->errno = 21;
return false;
}
if ($this->savePathFunc) {
$savePathFunc = $this->savePathFunc;
$this->savePath = $savePathFunc ( $this->saveName );
$this->returninfo ['path'] = $this->savePath;
}
$this->makeDirectory ( $this->savePath );
if (! @is_writable ( $this->savePath )) {
@mkdir ( $this->savePath, 0777, true );
}
if ($this->overwrite == 0 && @file_exists ( $this->savePath . $this->saveName )) {
$this->errno = 13;
return false;
}
if ($this->maxSize != 0) {
if ($fileArray ["size"] > $this->maxSize) {
$this->errno = 14;
return false;
}
}
if (! @copy ( $fileArray ["tmp_name"], $this->savePath . $this->saveName )) {
$this->errno = $fileArray ["error"];
return false;
}
}


这里先做了$this->validateFormat (),根据文件名来获取后缀,再判断后缀是否合法:

function validateFormat() {
if (! is_array ( $this->fileFormat ) || in_array ( strtolower ( $this->ext ), $this->fileFormat ) || in_array ( strtolower ( $this->returninfo ['type'] ), $this->fileFormat ))
return true;
else
return false;
}


关键看这个条件:in_array ( strtolower ( $this->returninfo ['type'] ), $this->fileFormat ),这里判断type是否合法而且用了或操作,等于这边为true了,整个if条件
就为true了, 而这个type我们可以改动的,只要抓包把
Content-Disposition: form-data; name="name"; filename="1.php"
Content-Type:
中的Content-Type设置成我们想要的值就可以绕过了。
绕过了这个地方以为后面就一帆风顺了,但是还是上传不上去,继续看下面的一个操作$this->fileFilter($fileArray ["tmp_name"],$this->ext)
这个操作实际上是根据文件头来确定文件的后缀,再检测后缀与之前的文件名获取的后缀是否一致。 这本来是一个很好的过滤方法,但开发人员又写错了:

function fileFilter($path,$ext){
if(keke_file_class::get_file_type($path,$this->ext)==$ext){
return true;
}else{
return false;
}
}
static function get_file_type($file_path, $ext = '') {
$fp = fopen ( $file_path, 'r' );
$bin = fread ( $fp, 2 );
fclose ( $fp );
$strInfo = @unpack ( "C2chars", $bin );
$typeCode = intval ( $strInfo ['chars1'] . $strInfo ['chars2'] );
$fileType = 'unknown';
$typeCode == '3780' && $fileType = "pdf";
$typeCode == '6787' && $fileType = "swf";
$typeCode == '7784' && $fileType = "midi";
$typeCode == '7790' && $fileType = "exe";
$ext == 'txt' && $fileType = "txt";
in_array ( $typeCode, array ('8297', '8075' ) ) && $fileType = $ext;
if (in_array ( $typeCode, array ('255216', '7173', '6677', '13780' ) )) {
in_array ( $ext, array ('jpg', 'gif', 'bmp', 'png', 'jpeg' ) ) and $fileType = $ext or $fileType = 'jpg';
}
if ($typeCode == '208207') {
in_array ( $ext, array ('wps', 'ppt', 'dot', 'xls', 'doc', 'docx' ) ) and $fileType = $ext or $fileType = 'doc';
}
return $fileType;
}


关键看这个操作:in_array ( $typeCode, array ('8297', '8075' ) ) && $fileType = $ext;
如果typecode 等于8297或者8075的时候,就会将filetype赋值为$ext,这样不就是饶过了之前的那个if判断。
POC: 只需要将content-type 设置成jpg 再在上传的文件开头写上Ra 就可以成功绕过上传过滤。 (Ra 获取以后的code值就是8297)

BaiduHi_2014-11-7_16-5-27.png


无标题.png


BaiduHi_2014-11-7_16-1-8.png

漏洞证明:

BaiduHi_2014-11-7_16-5-27.png


无标题.png


BaiduHi_2014-11-7_16-1-8.png

修复方案:

版权声明:转载请注明来源 路人甲@乌云


漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝