Laravel 5 Deserialization Chain Summary

2020年8月19日 242点热度 0人点赞 0条评论

图片

图片
前言

Laravel 7中由于一些有所类修复,导致一些pop chain无法使用,于是这次在Laravel 5系列中,也做一次总结,列举比较适合的切入点和查找新链的思路。

图片
遍地撒网

为了更好的找出切入点,我这里直接写了一个脚本,列举出所有包含__destruct的class和其__destruct的定义,并将laravel 5和laravel 7进行比对:

图片

其实不难发现,Laravel 7和Laravel 5在切入点这一块,并无太多的区别,几乎一致,一般修改均为一些微调。

同时我们可以搜寻一下切入点,一般分为如下几类:

· __destruct中$this->xxxx()调用形式

· __destruct中$this->xxx->yyy()调用形式

· __destruct中built-in function调用形式

那么本文对于laravel 5的pop chain寻找也围绕这3点进行展开。

图片
$this->xxxx()调用形式

根据这个调用形式进行寻找,有比较知名的CVE-2019-9081,我们可以看到其函数定义:

Illuminate\Foundation\Testing\PendingCommand::__destruct

public function __destruct()

{

    if ($this->hasExecuted) {

        return;

    }

    $this->run();

}

此处run函数可以引入RCE风险,此处分析不再赘述,可以参考文章:

https://laworigin.github.io/2019/02/21/laravelv5-7%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96rce/

当然这个类在Laravel 7中已经被修复。

除此之外,还有包括上篇文章我们分析过的:

Illuminate\Routing\PendingResourceRegistration::__destruct

public function __destruct()

{

    if (! $this->registered) {

        $this->register();

    }

}

此处register函数可以引入RCE风险,也不再赘述,可以参考上一篇文章。

类似的调用情况同样很多,我简单列举几个:

GuzzleHttp\Cookie中常见的:

$this->save();

Monolog\Handler和Symfony\Component中常见的:

$this->close();

League\Flysystem中常见的:

$this->disconnect()

除此之外,还有一些在__destruct中出现频率不高的,如果感兴趣的都可以跟进进行尝试构造。

图片
$this->xxx->yyy()调用形式

而对于这种调用形式,我们在之前的文章中提到过,其有2种思路进行利用:

· __call魔法方法

· 同名函数

我们看几个典型的例子:

Illuminate\Broadcasting\PendingBroadcast::__destruct

public function __destruct()

{

    $this->events->dispatch($this->event);

}

此处由于$this->events和$this->event均可控,因此可利用同名函数或__call的方式进行RCE pop chain的构造。

除此之外:

Symfony\Component\Routing\Loader\Configurator\ImportConfigurator::__destruct

public function __destruct()

{

    $this->parent->addCollection($this->route);

}

同样有着相似的问题,虽然可能没有同名危险函数,但可以利用__call来进行构造,配合Faker\Generator来构造RCE pop chain。

并且如下类也存在类似的问题:

Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator::__destruct

public function __destruct()

{

    if (null === $this->prefixes) {

        $this->collection->addPrefix($this->route->getPath());

    }

        $this->parent->addCollection($this->collection);

}

相应的,其实我们在构造同名函数RCE pop chain的时候其实还算好,但当构造__call的时候,由于call name一般不可控,毕竟Faker\Generator中name可通过数组控制的情况不算特别多,那么此时可能会遇到瓶颈。

所以这种形式的利用手段并不是想象中那么丝滑(,还是需要精心构造的。

图片
built-in函数

此类情况一般偏少,我们将搜寻锁定在敏感函数上,例如:

call_user_func、call_user_func_array、system、eval......

这里不难直接发现一个类:

GuzzleHttp\Psr7\FnStream::__destruct

public function __destruct()

{

    if (isset($this->_fn_close)) {

        call_user_func($this->_fn_close);

    }

}

我们发现其直接调用了call_user_func,同时参数可控,为 $this->_fn_close,但难点在于该函数只可控第一个参数,因此这里我们可以想到能否调用类内方法,如果该方法不需传递参数且方法内敏感函数参数可控,为类内属性,那么即可利用。

这里不难想到,诸如:Illuminate\Foundation\Testing\PendingCommand的run方法,Illuminate\Routing\PendingResourceRegistration的register方法,都是可以通过其进行利用的。

当然这会显得有些取巧,如果你有兴趣的话,可以过一遍危险函数所在的方法,看看是不是其可以无参调用~

但是不幸的是,当前这个例子中,我们跟进类进行分析:

public function __wakeup()

{

    throw new \LogicException('FnStream should never be unserialized');

}

由于存在__wakeup,我们在利用这个chain的时候会抛出'FnStream should never be unserialized'的错误,而导致无法利用。

当然,我们也可以不仅仅找__destruct函数内的危险函数,尝试搜寻一些危险函数所在的方法和类,不难找到如下几个情况:PHPUnit\Framework\MockObject\Stub\ReturnCallback::invoke,关键代码如下:

public function invoke(Invocation $invocation)

{

    return \call_user_func_array($this->callback, $invocation->getParameters());

}

又如Mockery\Loader\EvalLoader::load,关键代码如下:

public function load(MockDefinition $definition)

{

    if (class_exists($definition->getClassName(), false)) {

        return;

    }

        eval("?>" . $definition->getCode());

}

诸如此类情况,我们都可以将其整合进call_user_func或者call_user_func_array可控2个参数的地方,例如和Illuminate\Broadcasting\PendingBroadcast::__destruct组合,构造新的chain。

图片
后记

Laravel 5由于过滤相对于Laravel 7来说缺失了一些,因此更容易被组建pop chain,同时laravel由于提供了大量的可用于构造的模块,也会衍生出各种排列组合的pop chain,但万变不离其中,最关键的还是寻找切入点。

本文提出的一些寻找pop chain的思路也是抛砖引玉,实际上寻找切入点的方式远远不止__destruct和文中所提及的3种类型,如果你有好的想法也欢迎和我联系交流~

总之还是那句话,求求CTF里别再出laravel的pop chain构造了。

图片

图片

17160Laravel 5 Deserialization Chain Summary

root

这个人很懒,什么都没留下

文章评论