Fight For Freedom

11月 01

PHP Object Injection

       php对象注入(php Object Injection),在我看来是一种比较有意思的漏洞。不要感到陌生,只是我们常把它称为反序列化漏洞。

0x01 预备知识

       php序列化操作serialize()是为了将对象或数组等转变为正常的字符串,用于保存内容或传参数等等。那么反序列化unserialize()是其相反的操作,将一个序列化好的字符串作为输入,返回原来的数组或对象等,但其中需考虑到对象实例化和自动加载。也就是说,反序列化可能会导致代码被执行。

       对象实例化,当调用new class()时,class()就成为了一个实例化的对象。当反序列化一个字符串时,而这正是PHP所做的(对象实例化),就会恢复原来的数组或对象。反序列化对象允许控制所有属性:public、protected和private。

      那么什么情况下对象实例化后函数或方法会被自动加载?php中具有这样的特性的函数通常被称为魔幻函数或魔幻方法。它们可以被自动地调用,所以这些函数不需要通过函数调用来执行其代码。比如常见的魔幻函数:__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString()...


0x02 漏洞详情

       举个例子:反序列化对象通过__construct()初始化,通过__wakeup()苏醒,执行完后有__destruct()销毁,那么在这些魔幻函数中存在的代码是会被执行的,而这些代码又恰好被我们控制的话,漏洞就产生了。根据不同的内容可以导致多种多样的攻击,比如代码注入,sql注入,目录遍历和拒绝服务等。

      漏洞的根源在于可控参数传递给unserialize()函数没有采取合理地过滤。成功利用需要两个条件:

      1. 应用程序中必须含有一个实现某个PHP魔幻方法(例如__wakeup或者__destruct)的类,可以用这个类进行恶意攻击,或者开始一个“POP链”;

      2. 当调用脆弱的unserialize()时,必须声明攻击期间所使用的所有类,否则必须为这些类支持对象自动加载。
     
0x03 反序列化之pop chain
      

       终于讲到这次的有趣点了:pop链

       我开始看到的第一反应也跟如下图所示:

 

    

       其实跟rop不一样,虽然都是串链一步一步达到漏洞。

       当可控点经过反序列化后,但关键的代码执行函数不是魔幻函数,所以就无法自动调用,得不到执行,我们就需要一种办法既可以让其自动调用,也可以使输入参数恶意代码达到相应的目的。      

       我们把魔幻函数当作是最开始的gadgets,它们会调用其它的函数(gadgets),我们通过寻找相同名字的函数,将类的属性和敏感函数的属性联系起来,即pop chain。所有的敏感属性我们都是可以控制的。

       下面我写了个例子来详细说明有趣的技术:

       

<?php 
    class example2{
        protected $handle;
        function __destruct(){
            $this->shutdown();
        }
        public function shutdown(){
            $this->handle->close();
        }
    }

    class Process{
        private $pid;  
        function close(){
            eval($this->pid);
        }
    }

    if(isset($_GET['data'])){
        $user_data = unserialize($_GET['data']);
        //var_dump($user_data);
    }
?>


       从代码中,我们可以看到可以控制Process类的pid,但是却无法将其close函数自动调用。但是example2中却含有魔幻函数,并且调用了handle中的close,我们可以控制handle属性为任意对象,然后去执行close函数。由此:

    

<?php 
    class Process{
        private $pid;
        function __construct(){
            $this->pid = "phpinfo();";
        }
    }

    class example2
    {
        protected $handle;
        function __construct(){
            $this->handle = new Process;
        }
    }
    print urlencode(serialize(new example2));

?>

       我们使example2的handle指向Process对象的实例化,而Process中的pid附上我们的恶意代码。

       为了更加清楚的看到执行过程,我增加了一些调试语句:

      

<?php 
    class example2{
        protected $handle;
        function __destruct(){
            echo '1<br >';
            $this->shutdown();
        }

        public function shutdown(){
            echo '2<br >';
            $this->handle->close();
        }
    }

    class Process{
        private $pid;  
        function close(){
            echo '3<br >';
            eval($this->pid);
        }
    }

    if(isset($_GET['data'])){
        $user_data = unserialize($_GET['data']);
        var_dump($user_data);
    }
    echo 'ok<br >';
?>

        最终效果图:

       

 

      在做实验的过程中,我想构造命令执行的漏洞,用system执行不了,却可以执行其它的php函数(比如eval、echo等)。

      (后记:已解决,是自身实验环境原因。话说一年来,感觉unserialize就是个坑,不知贡献多少个cve了。)

 

0x04 漏洞挖掘

       讲了这么多,那么此漏洞应该如何被挖掘呢。

       1. 观察输入的数据来源是否被反序列化;

       2. 搜索魔幻函数,观察有漏洞的脚本是否包含这个类。php中提供了get_included_files(),可以用来得到该脚本包含的文件。所以,我们在serialize调用前加上get_included_files()查看已包含的脚本,观察有漏洞的类是否在其中。

 

0x05 防范

       不要用serialize()/unserialize(),使用json_encode()/json_decode() 也可以达到相同的目的。如果你使用了序列化,那么就不要将用户提交的数据传到unserialize()中去。

 

 

参考来源:

Code Reuse Attacks in PHP:Automated POP Chain Generation

标签:none

已有 2 人抢先你了

  1. 过客 过客

    虽然博主文章过了好久了,本地system测试还是可以执行的

    1. 恩,重测了下是可以的。可能当时一些危险函数被我禁掉了。
      thx

添加新评论

captcha
请输入验证码

最新文章

最近回复

  • lynahex:十分感谢解惑,特别是解决问题的方法。
  • ld:1. 搜索CVE-2016-0701 进入 https://cv...
  • lynahex:好的。
  • xdxd:友链已加~~~希望有机会多多交流~~
  • lynahex:恩,重测了下是可以的。可能当时一些危险函数被我禁掉了。 thx
  • 过客:虽然博主文章过了好久了,本地system测试还是可以执行的
  • 友情链接

    分类

    其它