安全矩阵

 找回密码
 立即注册
搜索
查看: 3001|回复: 0

ClassCMS1.3任意文件上传漏洞分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-10 16:58:27 | 显示全部楼层 |阅读模式
原文链接:ClassCMS1.3任意文件上传漏洞分析

0x01 前言
之前在某CMS也接触过自解压导致任意文件上传的漏洞,只要对请求URL过滤不严谨,以及逻辑处理存在缺陷便可导致上传可控,直接一步获取服务器权限。本次通过分析ClassCMS源码,从原理上理解此类漏洞。代审小白,分析不到位请大佬们多多体谅。
0x02 环境搭建
1、首先准备phpstudy,这里使用的是2016版本的,php版本为5.6.27

2、部署cms源码

3、访问http://192.168.150.9/index.php进行安装

4、输入相应配置信息,安装成功(数据库需提前新建好)

0x03 漏洞复现
1、输入账号密码登录进后台


2、进入应用商店主页

3、选择任意应用,点击下载

4、进入具体应用页面后,点击下载并抓包

可以看到第二个数据包中通过url穿参一个链接

5、解码后可以看到是向主页请求下载一个1.0版本的东西

6、访问一下可以看到是个zip压缩包

7、放包后查看一下返回结果


8、准备一个包含一句话木马的PHP文件,并进行压缩

9、通过python开启一个http服务

​​

10、替换请求的URL链接为1.zip的链接,修改classhash的值,这个将作为文件夹的名字,所以最好取个不会重复的


11、一句话吗木马上传成功

12、执行命令
访问:http://192.168.150.9/class/test123/1.php?1=phpinfo();

0x04 漏洞分析1、首先还是一样,先看入口文件index.php

2、定义那些可以先不用看,关键包含了一个cms.php文件

3、里面有好多方法,这里就不细细分析了,大体是定义了许多方法,在需要的时候进行调用。在同一目录下找到一个route.php文件

4、路由规则简单理解了,文件:方法:参数,回过头来看请求的URL

定位到shop/shop.php的downloadClass方法

5、路由定位到了,那么开始通读函数(因为会远程加载一些方法,所以将原有的代码段进行了分段),注释中为分析过程。
  1. function downloadClass() {
  2.             //是否传参classpath,否则返回空,这里过
  3.         if(!is_hash(@$_POST['classhash'])) {
  4.             Return ;
  5.         }
  6.         $classhash=$_POST['classhash'];
  7.         $url=$_POST['url'];

  8. //调用cms目录下的class.php文件中的get方法,检测是否存在同名的classhash
  9.                 if(C('cms:class:get',$classhash)) {
  10.             echo(json_encode(array('msg'=>'应用已存在','error'=>1)));
  11.             Return ;
  12.         }
  13.                 //检测是否有curl_init函数
  14.         if (!function_exists("curl_init")){
  15.             echo(json_encode(array('msg'=>"服务器未安装Curl组件,无法下载应用文件",'error'=>1)));
  16.             Return ;
  17.         }
  18.                 //检测是否有zip_open或ZipArchive函数
  19.         if(!function_exists('zip_open') || !class_exists('ZipArchive')) {
  20.             echo(json_encode(array('msg'=>"未安装zip组件,无法解压安装包",'error'=>1)));
  21.             Return ;
  22.         }
  23.                 //解压后文件存放的路径
  24.         $classdir=classDir($classhash);
  25.                 //赋值缓存目录路径
  26.         $path=$GLOBALS['C']['SystemRoot'].$GLOBALS['C']['CacheDir'].DIRECTORY_SEPARATOR.'shop'.DIRECTORY_SEPARATOR;
  27.                 //查看目录是否存在,不存在则使用@mkdir进行创建
  28.         if(!cms_createdir($path)) {
  29.             echo(json_encode(array('msg'=>"创建缓存目录失败,无法下载",'error'=>1)));
  30.             Return ;
  31.         }
  32.                 //缓存文件,后面将会被删除
  33.         $classfile=$path.md5($classhash.time()).'.class';

  34. //跳转到本文件到download函数中,后面解释
  35.         if(!C('this:download',$url,$classfile)) {
  36.             echo(json_encode(array('msg'=>"下载失败",'error'=>1)));
  37.             Return ;
  38.         }
  39.                 //不存在post md5,过
  40.         if(isset($_POST['md5']) && !empty($_POST['md5']) && function_exists("md5_file")) {
  41.             if($_POST['md5']!=@md5_file($classfile)) {
  42.                 echo(json_encode(array('msg'=>"文件校验失败,请重新下载",'error'=>1)));
  43.                 Return ;
  44.             }
  45.         }
复制代码


6、很标准的一个使用http下载文件的函数,curl_init创建实例,curl_setopt设置参数,curl_exec抓取、curl_close关闭。代码中通过这个函数获取搭建好的web服务中的zip文件

  1. //调用cms目录下的class.php文件中的unzip方法,这里返回true
  2.         if(C('cms:class:unzip',$classfile,$classdir)) {
  3.             //返回true,使用unlink删除文件
  4.             @unlink($classfile);
  5.             //调用cms目录下的class.php文件中的refresh方法
  6.             if(C('cms:class:refresh',$classhash)) {
  7.                 echo(json_encode(array('msg'=>"下载完成,请在应用管理页面中安装此应用")));
  8.                 Return ;
  9.             }else {
  10.                 echo(json_encode(array('msg'=>"安装包格式错误,请重试",'error'=>1)));
  11.                 Return ;
  12.             }
  13.         }else{
  14.             //返回false,也使用unlink删除文件
  15.             @unlink($classfile);
  16.             echo(json_encode(array('msg'=>"安装包解压失败,请重试",'error'=>1)));
  17.             Return ;
  18.         }
  19.         Return ;
  20.     }
复制代码


7、在cms目录下的class.php文件中定位unzip方法,这里使用ZipArchive或者zip_open进行zip包的解压缩,未进行过滤

8、在cms目录下的class.php文件中定位refresh方法

9、首先第一个便是调用is_hash()方法,在cms/cms.php文件中找到is_hash方法

10、具体利用我在在线php代码执行中测试了,这也是一个好方法,在无法确定的时候,通过执行结果来判断。这里很明显没有进入到if判断中。
  1. <?php

  2. function is_hash($hash) {
  3.     Return preg_match('/^[A-Za-z]{1}[A-Za-z0-9_]{0,31}$/',$hash);
  4. }
  5. if(!is_hash("test123")) {echo "123";}
  6. else{echo "is hash";}
复制代码



11、然后使用is_file函数检测文件,因为创建的目录名为test123,文件名为1.php,所以文件不存在,这里直接返回false。即在源代码中执行一下代码
  1. else {
  2.                 echo(json_encode(array('msg'=>"安装包格式错误,请重试",'error'=>1)));
  3.                 Return ;
  4.             }
复制代码


12、与验证漏洞过程相匹配

0x05 总结
从一个利用poc到分析源码,需要从路由,到定位关键漏洞路径,到漏洞利用经过到链,最后到执行,其实只要能够每个函数都进行分析,再从数据处理那一块都角度进行,便会发现比想象中容易些。分享一个小tips,在一些PHP代码无法直接判断的时候,可以通过将代码拿出来,自己执行一遍进行判断。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 00:33 , Processed in 0.014064 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表