zend framework3 反序列化漏洞复现分析
环境安装
composer下载
composer create-project zendframework/skeleton-application
然后进入目录 php -S 0.0.0.0:8099 -t public 启动

修改module/Application/src/Controller/IndexController.php

生成poc,poc.php

发送请求,环境搭建成功

漏洞复现分析:
查看POC
根据POC进行分析
-
Zend\Http\Response\Stream

__destruct方法中的unlink ,unlink函数的第一个参数为String类型,如果$this->streamName为一个类,即可触发__toString方法 -
根据PoC对Zend\View\Helper\Gravatar进行分析

__toString调用了getImgTag方法,跟进getImgTag

跟进setSrcAttribForImg,就是设置attributes的值,这个值是可控的,attributes需要为一个数组

跟进htmlAttribs,该方法有一个attributes,为 getImgTag传入

继续跟进$this->getView(), AbstractHelper->getView

AbstractHelper类只有get set,先跟进plugin,PhpRenderer-> plugin,所以$this->getView()需要为PhpRenderer类

跟进PhpRenderer有plugin方法
$__helpers
跟进getHelperPluginManager,


$__helpers也是可以控制的,也$this->getHelperPluginManager()可以控制

4.分析POC $__helpers需要Zend\Config\ReaderPluginManager。跟进ReaderPluginManager,该类中没有get方法,但是该类继承AbstractPluginManager,跟进AbstractPluginManager,get方法

Has方法的$name为escapehtml
,
$options不可控只能为空,所以$instance从parent:get取值,跟进parent:get,ServiceManager->get
该方法返回$this->
services数组,继续跟进validate,

发现services和instanceOf都可控,但是由于
$escaper = $this->getView()->plugin('escapehtml');
$escapeHtmlAttr = $this->getView()->plugin('escapehtmlattr');
所以在序列化ReaderPluginManager时候需要services的key为escapehtml和escapehtmlattr,$this->instanceOf需要为Zend\Validator\Callback(),最终
$escaper为$instanceOf,也就是Zend\Validator\Callback(),所以当运行到$escaper($key)时候会调用__invoke

然后调用Callback 的isValid。跟进isValid


139行有个call_user_func_array , $args为1,
$callback = $this->getCallback(),跟进getCallback


$options也可以控制,在序列化时候Callback指定options的值,最后导致远程命令执行
所以整体利用链如下:

-
Zend\Http\Response\Stream->__destruct()中的unlink()触发Zend\View\Helper\Gravatar->__toString()方法
-
Zend\View\Helper\Gravatar->__toString()调用Zend\View\Helper\Gravatar->getImgTag()方法,最后触发了
AbstractHtmlElement.php:73的htmlAttribs方法
htmlAttribs中的$this->getView()->plugin为PhpRenderer的plugin,$this->getView(),PhpRenderer->plugin调用了getHelperPluginManager,etHelperPluginManager的__helpers可控
控制__helpers需要为Zend\Config\ReaderPluginManager,Zend\Config\ReaderPluginManager,没有get方法ReaderPluginManager继承AbstractPluginManager,
所以调用了AbstractPluginManager->get,后续调用了ServiceManager->get,AbstractPluginManager继承,ServiceManager,services和instanceOf可控,
所以只需要序列化ReaderPluginManager时指定services和instanceOf,$escaper等于AbstractPluginManager->get返回的instanceOf,也就是Zend\Validator\Callback(),当$escaper($key)的时候会触发Zend\Validator\Callback->__invoke(),终执行Zend\Validator\Callback- isValid()中的call_user_func_array
写的不好勿喷。。
参考连接:
