TP-5.24-反序列化复现

前言

TP代码审计一直是个坎,很多次CTF比赛中都会出现,都不是特别拿手,所以简单复现TP的经典漏洞,也算是学习记录。

复现

环境

官方源码下载即可

控制器修改代码

<?php

namespace app\index\controller;

class Index
{
    public function index()
    {
        echo ("Welcome");
        unserialize($_GET['payload']);
    }
}

分析

入口函数,全局搜索 __destruct

windows.php

image-20211206150939171

跟进removeFiles

image-20211206151428681

这里的file_exists()函数会触发__toString()方法

全局搜索 __toString,找到Model

image-20211206152859164

跟进toJson

image-20211206152922010

跟进toArray

image-20211206153600952

发现了魔法函数 __call利用点,

如果valueattr 可以控制,就可以触发__call方法,

回溯法,判断变量是否可控

那么怎样才可以执行这条语句呢?

七大关

1.!empty($this->append) # $this->append不为空
2.!is_array($name) #$name不能为数组
3.!strpos($name, '.') #$name不能有.
4.method_exists($this, $relation)#$relation必须为Model类里的方法
5.method_exists($modelRelation, 'getBindAttr')#$modelRelation必须存在getBindAttr方法
6.$bindAttr #必须有值
7.!isset($this->data[$key]) #$key不能在$this->data这个数组里有相同的值。

这里的$this->append可控,并且把值赋给了name,所以前3关过了

第四关,判断$relation必须为Model类里的方法

溯源$relaiton

$relation = Loader::parseName($name, 1, false);

parseName此函数,只是将 $name小写转换赋给$relation,所以$relation可控

由于

$modelRelation = $this->$relation();

要是$modelRelation可控,所以要找既在Model类,也要可以控返回值的方法,找到了getError函数

image-20211208102041055

继续跟

 $value = $this->getRelationData($modelRelation);

跟进getRelationData

image-20211207233832062

注意:$modelRelation必须为Relation对象,通过$this->error控制,并且$modelRelation这个对象还要有isSelfRelation()getModel()方法,

搜索这两种方法发现Relation类中都有,但因为Relation为抽象类,需要寻找他的子类。全局搜索。

除了最后一个是抽象类外,都可以拿来用,但是!!!,我们别忘了第五关,需要$modelRelation必须存在getBindAttr方法,但是Relation没有getBindAttr方法,只有OneToOne类里有,且OneToOne类正好继承Relation类,不过是抽象类,所以我们需要找它的自类。全局搜索

发现存在两个可用的,我们选择第二个HasOne,即$this->error=new HasOne()。好了,调用方法的问题解决了,但是还需要过三小关

总结一下$modelRelation的要求

$modelRelation必须为Relation对象
要有`isSelfRelation()`、`getModel()`方法
必须存在getBindAttr方法

只要满足 if,value便可控

if ($this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent)) {
            $value = $this->parent;
}

$this->parent可控,我们要使用Output类中的__call,所以$value必须为output对象,所以$this->parent必须控制为output对象,即$this->parent=new Output().

跟进isSelfRelation

image-20211207234228903

可控,跟进$modelRelation->getModel()

image-20211207234330822

get_class()返回对象实例 obj 所属类的名字

$this->parent已经确定为Output类了,所以我们要控制get_class($modelRelation->getModel())为Output类

query可控,所以全局找有getModel(),并且可以控制返回值的对象

在query.php中找到

image-20211208103840885

if满足,value赋值成功

第六关

$bindAttr = $modelRelation->getBindAttr();

跟进getBindAttr,可控。

第七关

!isset($this->data[$key]) #$key不能在$this->data这个数组里有相同的值。

key值溯源,$bindAttr 可控,所以可控。

调用Output的__call方法

image-20211208104755120

此时 method = getAttr(),args=$bindAttr,

this->styles可控,所以让this->styles == getAttr 即可

array_unshift函数将method插入到args的头部

跟进block方法

image-20211208110641330

跟进writeln

image-20211208110744377

handle可控,全局找write方法

找到memcached的write方法

image-20211208111129783

跟进set

image-20211208111341560

file_put_contents写webshell操作,data由value控制,但是value=true,不可控。

跟进getCacheKey

image-20211208113403427

由于name是可控制的,所以

调用了setTagItem函数,跟进

image-20211208113147817

重置了value,但是这次name变量是可控的,所以value可控,继续调用set方法,这次set的两个参数都是可控的,key进入了getCacheKey函数,发现option['path']可控,而且它的值会作为二次调用set函数中的value值,写入到文件中,则此函数的返回值可控。

绕过exit()死亡函数,写入shell即可。

总结

  1. 先找漏洞点,敏感危险函数是入门的第一步。

  2. 学习了新的审计方法:

    1.找到了危险函数,列举执行危险函数需要的条件,然后一步一步满足

  3. PHP语言中类的继承和抽象类的继承特性

1.抽象类继承抽象类时,不能重写父类中的抽象方法

​ 2.非抽象类继承抽象类时,必须实现抽象类中的所有方法

审计过程中,如果出现方法在抽象类中,不能直接实例,要找继承抽象类的非抽象子类

3.至少有一个抽象方法的类就是抽象类

  1. 温顾了以前的trick:

    file_exists()函数会触发__toString()方法

    绕过死亡函数写入shell。

参考————两条链子

https://www.freebuf.com/articles/web/284091.html

上面这个审计方法很不错!值得学习

较为简单的另一条链子https://kath4rs1s.github.io/2020/09/13/thinkphp5-rce/

posted @ 2022-02-06 17:34  kzd的前沿思考  阅读(73)  评论(0编辑  收藏  举报