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
写的不好勿喷。。
参考连接: