PHP反序列漏洞學習
0x00 序列化和反序列化
在PHP中,序列化和反序列化對應的函數分別為serialize()和unserialize()。
序列化:serialize()將對象轉換為字符串以便存儲傳輸的一種方式。
反序列化:unserialize()將序列化以後產生的字符串轉換為對象供程序使用。
反序列化本身並不危險,但是如果在反序列化時,傳入反序列化函數的參數可以被用戶控制那將會是一件非常危險的事情。所以,反序列化的危害,關鍵還是在於可控或不可控。
0x01 PHP序列化格式
1、基礎格式
Boolean
b:;
b:1;// True
b:0;// False
Integer
i:;
i:1;//1
i:-4;//-4
Double
d:;
d:1.234560000000001;//1.23456(PHP弱類型造成的四舍五入現象)
NULL
N;
String
s::"";
s:4:"name";//name(4指的是name的字符長度)
Array
a::{key,value pairs};
a:2:{s:4:"key1";s:6:"value1";s:4:"key2";s:6:"value2";}// array("key1"=>"value1","key2"=>"value2")
2、序列化舉例
class_serializ.php
<?php class Person{ private $name = ‘Thinking‘; public $sex = ‘man‘; function say($name,$sex) { echo "My name is ".$name."I am a ".$sex; } } $Person = new Person(); echo serialize($Person); ?>
運行結果:
O:6:"Person":2:{s:12:"Personname";s:8:"Thinking";s:3:"sex";s:3:"man";}
O 代表object
6 代表對象名字Person占6個字符
Person 對象名
2 代表對象裏面有2個變量
s 變量數據類型,s代表string類型
12 代表變量名的字符長度
Personname 代表這是一個私有變量,變量名是name
Thinking 代表是變量name的值
3、反序列化實例
<?php class Person{ private $name = ‘Thinking‘; public $sex = ‘man‘; function say($name,$sex) { echo "My name is ".$name."I am a ".$sex; } } $Person = new Person(); $seri = serialize($Person); $unseri = unserialize($seri); var_dump($unseri) ?>
輸出結果
object(Person)#2 (2) { ["name":"Person":private]=> string(8) "Thinking" ["sex"]=> string(3) "man" }
0x02 PHP(反)序列化有關的魔法函數
**__construct()**
構造函數,當一個對象創建時被調用
**__destruct()**
析構函數,當一個對象銷毀時被調用
call(),callStatic()
方法重載的兩函數
__call()是在對象上下文中調用不可訪問的方法是時觸發
__callStatic()是在靜態上下文中調用不可訪問的方法是觸發
get(),set()
__get()用於從不可訪問的屬性讀取數據
__set()用於將數據寫入不可訪問的屬性
isset(),unset()
__isset()在不可訪問的屬性上調用isset()或empty()時觸發
__unset()在不可訪問的屬性上使用unset()是觸發
sleep(),wakeup()
__sleep()在對象被序列化之前運行
__wakeup()將在序列化之後立即被調用
**__toString()**
__toString()方法允許一個類決定如何處理像一個字符串時它將如何反應
這麽多的魔術方法中我們所需要關註的方法也就是__destruct() 和 __wakeup() 方法.這兩個方法中前者是在對象被銷毀時程序會自動調用,後者是在類對象被反序列化時被調用.所以這兩個方法是在 對象反序列化一直到程序執行完畢這整個過程中,必定會被調用的方法,如果在這兩個函數中有一些危險的動作,並且能夠被我們所利用,那麽漏洞並出現了。
舉個簡單的例子
test.php
<?php
class A{
var $test = "demo";
function __destruct(){
echo $this->test;
}
}
$a = $_GET[‘test‘];
$unser = unserialize($a);
?>
我們只要構造payload:
http://127.0.0.1/serialize/test.php?test=O:1:%22A%22:1:{s:4:%22test%22;s:5:%22hello%22;}
就能控制echo出的變量。
0x03 PHP反序列化與POP鏈
在反序列化中,我們所能控制的數據就是對象中的各個屬性值,所以在PHP反序列化有一種漏洞利用方法叫做“面向屬性編程”,即POP(Property Oriented Programming)。和二進制漏洞中常用的ROP技術類似。在ROP中我們往往需要一段初始化gadgets來開始我們的整個利用過程,然後繼續調用其他的gadgets。在PHP反序列化漏洞利用技術POP中,對應的初始化gadgets就是__wakeup()或者是__destruct()方法,在最理想的情況下能夠實現漏洞利用的點就在這兩個函數中,但往往我們需要從這個函數開始,逐步的跟進在這個函數中調用到的所有函數,直至找到可以利用的點為止。
下面列舉在跟進其函數調用過程中需要關註一些有價值的函數。
命令執行:
exec()
passthru()
popen()
system()
文件操作:
file_put_contents()
file_get_contents()
unlink()
PHP反序列漏洞學習