https://xz.aliyun.com/t/11531#toc-3
https://www.nssctf.cn/note/set/4490
因为在学习的过程中间发现自己必须学会审计一些代码,因为自己之前也没有认真的进行过审计,之前遇到了一个thinkphp的题目,但是直接利用了exp,没有仔细审过,所以这次我想仔细审一下,也算是希望自己能够真正进步和成长吧。
1、准备工作
就是使用composer安装
https://www.phpcomposer.com/
composer create-project topthink/think tp6 6.0.12
或者直接打开nssctf题目的环境访问/www.zip
下载源码,然后在本地用phpstorm
审。
https://www.nssctf.cn/problem/2347
2、找反序列化入口点__destruct
入口点是__destruct
,来触发下一步函数的执行
使用ctrl+shift+F
全局搜索__destruct,这里注意在搜索这一栏选择目录(D)
这一栏,如果选择在项目(P)
或者模块(M)
或者作用域(S)
这几栏的话会搜索不到,😅。
vendor\topthink\think-orm\src\Model.php
public function __destruct(){
if($this->lazySave){
$this->save();
}
}
这里我们按ctrl+B
可以追踪到save
public function save(array $data = [], string $sequence = null): bool
{
// 数据对象赋值
$this->setAttrs($data);
if ($this->isEmpty() || false === $this->trigger('BeforeWrite')) {
return false;
}
$result = $this->exists ? $this->updateData() : $this->insertData($sequence);
if (false === $result) {
return false;
}
// 写入回调
$this->trigger('AfterWrite');
// 重新记录原始数据
$this->origin = $this->data;
$this->get = [];
$this->lazySave = false;
return true;
}
我们从前往后看,其中$this->isEmpty()
和false === $this->trigger('BeforeWrite')
这两个式子只要一个为真,就会返回False
,所以需要让它们均为假才可以,即$this->isEmpty
为False,$this->trigger('BeforeWrite')
为true
我们先看第二个,我们再按ctrl+B
可以追踪到trigger
protected function trigger(string $event): bool
{
if (!$this->withEvent) {
return true;
}
$call = 'on' . Str::studly($event);
try {
if (method_exists(static::class, $call)) {
$result = call_user_func([static::class, $call], $this);
} elseif (is_object(self::$event) && method_exists(self::$event, 'trigger')) {
$result = self::$event->trigger('model.' . static::class . '.' . $event, $this);
$result = empty($result) ? true : end($result);
} else {
$result = true;
}
return false === $result ? false : true;
} catch (ModelEventException $e) {
return false;
}
}
$this->withEvent
为False就可以返回true
搜索withEvent
public function withEvent(bool $event)
{
$this->withEvent = $event;
return $this;
}
然后再看isEmpty
,最后是跟进到了empty()函数
public function isEmpty(): bool
{
return empty($this->data);
}
function PS_UNRESERVE_PREFIX_empty($var) {
}
empty中,参数是非空非零会返回false,下面这些都是空
""
(空字符串)0
(整型零)0.0
(浮点零)"0"
(字符串零)NULL
FALSE
- 一个空的数组
- 一个空的对象,包括类的对象
那只要让$this->data
不为空就可以,现在已经成功跳过第一个if,汗颜🥵💦
接下来来到一个三元运算符
$result = $this->exists ? $this->updateData() : $this->insertData($sequence);
意思就是如果this->exists
为true
,则执行updateData()
,反之,则执行$this->insertData($sequence)
,这里$this->exists
是可控的,先跟进updateData()
protected function updateData(): bool
{
// 事件回调
if (false === $this->trigger('BeforeUpdate')) {
return false;
}
$this->checkData();
// 获取有更新的数据
$data = $this->getChangedData();
if (empty($data)) {
// 关联更新
if (!empty($this->relationWrite)) {
$this->autoRelationUpdate();
}
return true;
}
if ($this->autoWriteTimestamp && $this->updateTime) {
// 自动写入更新时间
$data[$this->updateTime] = $this->autoWriteTimestamp();
$this->data[$this->updateTime] = $data[$this