前言
闲来无事 做了下队里大师傅出的CodeIgniter框架反序列化题目(大师傅自己挖的链)
在大师傅的指引下花费了几天终于调通了 学到了不少东西 想着自己能不能也挖下某些框架的链呢
于是找了下常用的php web框架 发现貌似CakePHP框架还没有公布什么链子 于是就有了此文
菜鸡第一次发文章 如有错误 请师傅们指出 轻点喷...
php调用函数的特性
php各版本在线运行网站:https://3v4l.org/
有参无参调用
demo:
<?php
class a{
public function aaa($a){
echo 'aaa';
}
public function cccc(){
echo 'cccc';
}
}
$a = new a();
$a->aaa();
$a->cccc(123,456);
php7.0.33运行结果:
虽然有个warning 但是两个函数都执行了
php7.1.0运行结果:
直接报Fatal error了 后面的代码都不执行了 调换下顺序 执行下试试
发现有参调用无参函数是不影响的
总结下:
- 无参函数可以有参调用
- 当php version ≥ 7.1.0,有参函数不能无参调用,会直接报Fatal error
- 当php version ≤ 7.0.33,有参函数可以无参调用,但会有个警告(Warning: Missing argument)
这个问题在奶权师傅挖的thinkphp3 反序列化利用链也有遇到
具体参见:https://mp.weixin.qq.com/s/S3Un1EM-cftFXr8hxG4qfA
反序列化入口
提到php反序列化,肯定首先要找入口,一般就__destruct方法和__wakeup这两个方法
刚开始找的时候直接用的目前最新版本(4.2.8)只发现了两处可以进一步利用的点(太菜 没找到其他口子)而且这两处还有限制
第一处
一处是位于: vendor\cakephp\cakephp\src\Mailer\Transport\SmtpTransport.php
跟进disconnect方法
$this->connected()的返回值是可控的
这里有可能触发__get方法 但实际上有个__wakeup方法直接把 $this->_socket 设置成空了
我们知道__wakeup方法是先于__destruct方法执行的 因此这里利用不了
再回头看下$this->_disconnect方法吧
跟进_smtpSend方法 此时$data不为空 进入_socket函数
由于__wakeup方法直接把 $this->_socket 设置成空 这里直接抛出异常了
因此这条路整体上就不通 其实这个对应一个CVE编号: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11458
官方的commit: https://github.com/cakephp/cakephp/commit/1a74e798309192a9895c9cedabd714ceee345f4e
对应修复的版本是3.7.7 4.x版本最开始就是已经修复的
详细的补丁信息:https://github.com/advisories/GHSA-qhrx-hcm6-pmrw
在版本小于上面patch的版本 这个类也可以作为入口 这里就不探讨了
第二处
位于: vendor\symfony\process\Process.php
__destruct方法中 貌似可以直接调用任意类的__call方法或者是调用含有close方法的类的close方法
然而比较新的版本仍然有个__wakeup方法限制 直接抛出异常了
往下找一下版本 如果直接在Github仓库直接找会发现是找不到这个类的 因为Github放的只是cakephp这个组件的代码 而这个类是symfony组件里面的
去releases下载完整的web项目包,在里面测试发现以下版本该类是没有__wakeup方法那个限制的
? < 4.x ≤ 4.2.3
? < 3.x ≤ 3.9.6
还有个问题就是 这个利用点在某些版本__destruct方法是不一样的
在比较老的版本中 大概是下面版本
? < 4.x ≤ 4.1.6
? < 3.x ≤ 3.9.4
__destruct方法是这样的
相当于新版本的进入了else分支 因此想新版旧版都通用可以直接进入else分支
跟进stop方法发现 进行了判断
跟进isRunning方法 $this->status可控
再跟进updateStatus方法 发现里面调用了proc_get_status函数 用来获取由 proc_open() 函数打开的进程的信息
这个函数我们随便设置个数字或者字符串之类的只有一个Warning 并不会终止
再跟进 发现有一个可控对象的readAndWrite方法调用 参数是两个bool值 这里我们就可以让其调用某个类的__call方法了
3.x某些版本利用
测试的时候选用的版本是3.9.6
__call进一步利用
上面可以知道 我们现在可以调用任意一个类的__call方法了 个人用的比较笨的方式 全局搜索__call方法 挨个看 然后就找到一个比较好的点
位于:vendor\cakephp\cakephp\src\ORM\Table.php
因此通过设置条件 应该可以进入call函数 全局搜索下call函数 有处:vendor\cakephp\cakephp\src\ORM\BehaviorRegistry.php
直接通过call_user_func_array调用的某个类的某个共有方法并传参数 这里类名、方法名均可空,只有参数不可控 回头捋下 发下$args就是我们最初调用__call的$args的值
再看下条件允不允许
首先第一个条件hasMethod方法
可控 注意转化成小写就行 $method就是触发__call用到的方法
第二个条件has方法 在其父类ObjectRegistry里 同样也是可控的
证明这条路是通的
寻找可利用的方法
现在就可以调用任意一个类的共有方法 只不过参数不可控罢了 这里我们就要想 要么那个方法的参数对后续操作没影响或者就那个方法就不接受参数
不卖关子了 直接上吧 位于:vendor\cakephp\cakephp\src\Shell\ServerShell.php
该类有个main方法 最终通过system函数执行命令
而命令是经过一系列拼接完成的 明显是可以命令注入的 简单测试下 linux直接用分号多语句执行 windows可以用&
但是在windows上利用需要有php环境变量 不然貌似不能执行 不知道为啥是这样 第一个命令不存在第二个命令貌似也不会执行成功 最后也没解决这个问题
真实情况下win应该比较少吧 2333... 后面还有个更好的链子 就不考虑这个问题了
接着让out方法正常执行 跟进来到了:vendor\cakephp\cakephp\src\Console\Shell.php
再跟进发现来到了:vendor\cakephp\cakephp\src\Console\ConsoleIo.php
可控 设置条件直接让这个返回true就行了
这样 整条链就串起来了
效果
poc就不贴了 避免一些不必要的问题
效果如下:
4.x某些版本利用
测试用的版本是4.1.6
替代ServerShell
4.x版本前面的整体思路和3.x基本一样 虽然可能部分代码有所改动
但是比较可惜的就是ServerShell这个类修改了
历史修改记录:https://github.com/cakephp/cakephp/commits/3.x/src/Shell/ServerShell.php
3.x从一开始就有这个类 从4.0开始就移除该类 从src/Shell/ServerShell.php更名到src/Command/ServerCommand.php
修改记录:https://github.com/cakephp/cakephp/commit/ec40d847d131b0d8aa9c8b7b6fd829570e01036d
而新修改的ServerCommand中没有和之前一样main方法一样好用的利用点了
没办法 只能寻找新的了 最后找到位于:vendor\cakephp\cakephp\src\Database\Statement\CallbackStatement.php
最后又一个动态调用 并且函数名可控 全局搜索fetch方法 有好多可利用点 最好选择cakephp命名空间下的
最后选用的是:vendor\cakephp\cakephp\src\Database\Statement\BufferedStatement.php
设置条件 即可实现上面的参数可控 fetch函数传过来的参数就是之前调用__call方法传过来的bool值 此条路通
这个类修改记录:https://github.com/cakephp/cakephp/commits/master/src/Database/Statement/BufferedStatement.php
因此应该是通杀2019年9月3日之后的版本的 3.x的某些版本也可以用
效果
效果如下:
总结
整条链顺下来 可能比较简单 但中间其实经过了很多次尝试 感觉过程中最难的就是找一个可以进入下一步来“扩大战果”的方法 本菜鸡是基于正则的方式来搜索的一点点看的2333 虽然笨但不失为一种解决问题的方法
当然 应该不止这一个链 中间的好多类或者是方法其实是可以替换使用的 师傅们有更好的思路可以留言或者私信本菜鸡!