ZendFramework的一些反序列化链
字数 2068 2025-08-07 08:22:05
ZendFramework 反序列化漏洞分析与利用
环境搭建
测试版本:
- ZendFramework 1.12.20
- PHP 5.6
环境搭建步骤:
- 下载ZendFramework: https://framework.zend.com/downloads/archives
- 创建项目:
zf.bat create project zendApplication - 在php.ini中添加include_path指向Zend目录
- 创建入口点文件:
zendApplication/application/controllers/IndexController.php
<?php
class IndexController extends Zend_Controller_Action {
public function init() {
unserialize(base64_decode($_POST['a']));
}
public function indexAction() {
// action body
}
}
漏洞链分析
1. 文件删除漏洞链 (FD1)
调用链:
Zend_Http_Response_Stream::__destruct()
细节分析:
- 入口点在
library/Zend/Http/Response/Stream.php的__destruct方法 - 所有参数可控,可直接删除文件
public function __destruct() {
if (is_resource($this->stream)) {
fclose($this->stream);
$this->stream = null;
}
if ($this->_cleanup) {
@unlink($this->stream_name); // 文件删除点
}
}
利用条件:
- 控制
_cleanup为true - 控制
stream_name为要删除的文件路径
2. 命令执行漏洞链 (RCE1)
调用链:
Zend_Log::__destruct() -> Zend_Log_Writer_Mail::shutdown() -> Zend_Layout::render() -> Zend_Filter_PregReplace::filter()
细节分析:
- 入口点:
Zend_Log::__destruct()
public function __destruct() {
foreach ($this->_writers as $writer) {
$writer->shutdown();
}
}
_writers可控,可调用任意类的shutdown方法
- 进入Zend_Log_Writer_Mail::shutdown()
public function shutdown() {
if (empty($this->_eventsToMail)) return;
if ($this->_subjectPrependText !== null) {
$this->_mail->setSubject("...");
}
$this->_mail->setBodyText(implode('', $this->_eventsToMail));
if ($this->_layout) {
$this->_layout->events = implode('', $this->_layoutEventsToMail);
$this->_mail->setBodyHtml($this->_layout->render());
}
$this->_mail->send();
}
- 需要设置:
_eventsToMail = "aa"(绕过空判断)_subjectPrependText = "null"(绕过null判断)_mail = new Zend_Mail()_layout = new Zend_Layout()
- 进入Zend_Layout::render()
public function render($name = null) {
if ($this->inflectorEnabled() && (null !== ($inflector = $this->getInflector()))) {
$name = $this->_inflector->filter(array('script' => $name));
}
// ...
}
- 设置
_inflectorEnabled = true _inflector不为空
- 最终进入Zend_Filter_PregReplace::filter()
public function filter($value) {
return preg_replace($this->_matchPattern, $this->_replacement, $value);
}
- 控制
_matchPattern和_replacement实现命令执行 - 示例:
_matchPattern = "/.*/e",_replacement = "system('id');"
3. 命令执行漏洞链 (RCE2)
调用链:
Zend_Http_Response_Stream::__destruct() -> Zend_Form_Element::__toString() -> Zend_Form_Element::render() -> Zend_Form_Decorator_Form::render() -> Zend_Cache_Frontend_Function::call()
细节分析:
- 入口点:
Zend_Http_Response_Stream::__destruct()
public function __destruct() {
if ($this->_cleanup) {
@unlink($this->stream_name); // 触发__toString
}
}
- 设置
_cleanup = true stream_name为Zend_Form_Element对象
- 触发Zend_Form_Element::__toString()
public function __toString() {
return $this->render();
}
- 进入Zend_Form_Element::render()
public function render(Zend_View_Interface $view = null) {
foreach ($this->getDecorators() as $decorator) {
$content = $decorator->render($content);
}
return $content;
}
- 控制
_decorators数组
- 进入Zend_Form_Decorator_Form::render()
public function render($content) {
$form = $this->getElement();
$view = $form->getView();
$helper = $this->getHelper();
$attribs = $this->getOptions();
$name = $form->getFullyQualifiedName();
return $view->$helper($name, $attribs, $content);
}
- 关键点:
$view->$helper($name, $attribs, $content) - 所有参数可控,可调用任意方法
- 最终进入Zend_Cache_Frontend_Function::call()
public function call($callback, array $parameters = array(), $tags = array(), $specificLifetime = false, $priority = 8) {
if (!$cache) {
return call_user_func_array($callback, $parameters);
}
// ...
}
- 控制
$callback和$parameters实现命令执行 - 绕过缓存检查条件:
(($cacheBool1 || $cacheBool2) && (!$cacheBool3))为false
关键点总结
-
文件删除漏洞:
- 利用
Zend_Http_Response_Stream的__destruct方法 - 完全可控,直接删除指定文件
- 利用
-
命令执行漏洞(RCE1):
- 通过
preg_replace的/e模式执行代码 - 需要构造复杂的对象关系链
- 通过
-
命令执行漏洞(RCE2):
- 通过
call_user_func_array执行任意函数 - 需要触发
__toString魔术方法 - 构造更复杂的对象关系,但功能更强大
- 通过
防御建议
- 升级到最新版本的ZendFramework
- 避免反序列化用户可控的数据
- 使用安全的反序列化方法,如JSON
- 实施输入验证和过滤
参考
- PHPGGC工具中已包含这些漏洞的利用链
- 实际利用时可参考PHPGGC中的具体Payload构造方法