xiaocms文件上传GETSHELL

漏洞描述

xiaocms后台文章发布存在缩略图上传. 该处并没有对文件类型进行校验. 导致可以上传任意文件, 直接getshell.

漏洞分析

通过xiaocms后台的文件上传URL可以得知URL是c=uploadfile&a=uploadify_upload&type=gif,jpg,jpeg,png

根据core/xiaocms.php中的parse_request()可以知道c参数是控制器,a参数是方法名, 可以定位到admin/contronller/uploadfile.php

而后根据a参数找到uploadify_upload方法定义的位置:

  1. /** 
  2. * uploadify_upload 
  3. */ 
  4. public function uploadify_uploadAction() { 
  5.      $type = $this->get('type'); 
  6.      $size = (int)$this->get('size'); 
  7.      if ($this->post('submit')) { 
  8.      $data = $this->upload('file', explode(',', $type), $size); 
  9.      if ($data['result']) echo $data['path']; 
  10.      } 
  11. }  

可以看见实际的文件上传动作是由$this->upload完成的, 对$this->upload传入了三个参数, 一个字符串常量'file' , 一个$type参数从URL中获取的, 然后一个文件大小$size, 其中$type是我们可以控制的, 这里没有对$type做任何处理.

我们继续跟入$this->upload看看它的实现:

  1. /** 
  2. * 文件上传 
  3. */ 
  4. private function upload($fields, $type, $size) { 
  5.     $upload = xiaocms::load_class('upload'); 
  6.     $ext = strtolower(substr(strrchr($_FILES[$fields]['name'], '.'), 1)); 
  7.     if (in_array($ext, array('jpg','jpeg','bmp','png','gif'))) { 
  8.         $dir = 'image'; 
  9.     } else { 
  10.         $dir = 'file'; 
  11.     } 
  12.     $path = $this->dir .$dir . '/' . date('Ym') . '/'; 
  13.     if (!is_dir(XIAOCMS_PATH.$path)) mkdirs(XIAOCMS_PATH.$path); 
  14.     $file = $_FILES[$fields]['name']; 
  15.     $filename = md5(time() . $_FILES[$fields]['name']) . '.' . $ext; 
  16.     $filenpath = $path.$filename; 
  17.     $result = $upload->set_limit_size(1024*1024*$size)->set_limit_type($type)->upload($_FILES[$fields],XIAOCMS_PATH.$filenpath); 
  18.     if (in_array($ext, array('jpg', 'gif', 'png', 'bmp'))) { 
  19.         $this->watermark(XIAOCMS_PATH.$filenpath); 
  20.     } 
  21.     return array('result'=>$result, 'path'=> SITE_PATH . $filenpath, 'file'=>$file , 'ext'=>$dir=='image' ? 1 : $ext); 
  22. }   

upload方法做了几个事情:

1. load_class 加载upload类

2. 从$_FILES中取出上传的文件名, 并且以.作为分隔符, 取出文件后缀.

3. 判断该上传文件是属于什么类型的. 图片类型,还是文件类型.

4. 然后建立上传目录, 重命名上传文件, 用上传文件名的后缀拼接重命名之后的文件名.

5. 设置上传文件大小, 设置上传文件类型, 然后调用upload方法上传文件, 对upload方法传入文件原始名称,和重命名之后的文件名称(路径)

6. 如果文件类型是图片就加上水印

7. 返回文件路径

我们先去看看upload方法长什么样子, upload方法在core/library/upload.class.php

  1. public function upload($file_upload, $file_name) 
  2. { 
  3.     if (!is_array($file_upload) || empty($file_name)) return false; 
  4.     $this->parse_init($file_upload); 
  5.     if (!@move_uploaded_file($this->file_name['tmp_name'], $file_name)) return '文件上传失败,请检查服务器目录权限'; 
  6.     return true; 
  7. }
可以看见 upload方法在正式 @move_uploaded_file之前, 调用了 parse_init()方法, 到现在为止,我们还没看见上传文件限制在哪里, 去看看 parse_init:

  1. protected function parse_init($file) 
  2. { 
  3.     $this->file_name = $file; 
  4.     if ($this->file_name['size'] > $this->limit_size) { 
  5.         echo '您上传的文件:' . $this->file_name['name'] . ' 大小超出上传限制!'; 
  6.         exit(); 
  7.     } 
  8.     if ($this->limit_type) { 
  9.         if (!in_array($this->get_file_ext(), $this->limit_type)) { 
  10.             echo '您上传的:' . $this->file_name['name'] . ' 文件格式不正确!'; 
  11.             exit(); 
  12.         } 
  13.     } 
  14.     return true; 
  15. } 
在这里我们看见了对上传文件的限制了. 在第24行到第28行. 可以看见它是根据 $this->limit_type来进行判断的.
如果 $this->get_file_ext的后缀不在 this->limit_type中, 就返回格式不正确.直接 exit()掉, 就无法进入下一步的 @move_uploaded_file
  1. public function set_limit_type($type) 
  2. { 
  3.     if (!$type || !is_array($type)) return false; 
  4.     $this->limit_type = $type; 
  5.     return $this; 
  6. }

$this->limit-type 在set_limit_type方法中定义, set_limit_size方法要求参数是$type. 通过联系上下文, 我们可以知道$type是直接从URL中获取的. 因此,$this->limit-type 我们是可控的. 可以上传任意文件.

漏洞利用

1. 登陆后台. 选择产品信息发布

2. 在缩略图上传处,选择文件上传. 载入1.php

3. 通过burp拦截请求, 在type后面加入,php. 上传成功, 获得返回地址


转载地址:http://eleveni386.7axu.com/blog/post/admin/xiaocms


你可能感兴趣的:(PHP)