PHP Session 序列化及反序列化處理器設定使用不當帶來的安全隱患

wyzsk發表於2020-08-19
作者: Ryat · 2014/11/14 15:05

PHP Session 序列化及反序列化處理器


PHP 內建了多種處理器用於存取 $_SESSION 資料時會對資料進行序列化和反序列化,常用的有以下三種,對應三種不同的處理格式:

處理器 對應的儲存格式
php 鍵名 + 豎線 + 經過 serialize() 函式反序列處理的值
php_binary 鍵名的長度對應的 ASCII 字元 + 鍵名 + 經過 serialize() 函式反序列處理的值
php_serialize
(php>=5.5.4)
經過 serialize() 函式反序列處理的陣列

配置選項 session.serialize_handler


PHP 提供了 session.serialize_handler 配置選項,透過該選項可以設定序列化及反序列化時使用的處理器:

session.serialize_handler "php" PHP_INI_ALL

安全隱患

透過上面對儲存格式的分析,如果 PHP 在反序列化儲存的 $_SESSION 資料時的使用的處理器和序列化時使用的處理器不同,會導致資料無法正確反序列化,透過特殊的構造,甚至可以偽造任意資料:)

$_SESSION['ryat'] = '|O:8:"stdClass":0:{}';

例如上面的 $_SESSION 資料,在儲存時使用的序列化處理器為 php_serialize,儲存的格式如下:

a:1:{s:4:"ryat";s:20:"|O:8:"stdClass":0:{}";}

在讀取資料時如果用的反序列化處理器不是 php_serialize,而是 php 的話,那麼反序列化後的資料將會變成:

#!php
// var_dump($_SESSION);
array(1) {
  ["a:1:{s:4:"ryat";s:20:""]=>
  object(stdClass)#1 (0) {
  }
}

可以看到,透過注入 | 字元偽造了物件的序列化資料,成功例項化了 stdClass 物件:)

實際利用


i)當 session.auto_start=On 時:

當配置選項 session.auto_start=On,會自動註冊 Session 會話,因為該過程是發生在指令碼程式碼執行前,所以在指令碼中設定的包括序列化處理器在內的 session 相關配選項的設定是不起作用的,因此一些需要在指令碼中設定序列化處理器配置的程式會在 session.auto_start=On 時,銷燬自動生成的 Session 會話,然後設定需要的序列化處理器,再呼叫 session_start() 函式註冊會話,這時如果指令碼中設定的序列化處理器與 php.ini 中設定的不同,就會出現安全問題,如下面的程式碼:

#!php
//foo.php

if (ini_get('session.auto_start')) {
    session_destroy();
}

ini_set('session.serialize_handler', 'php_serialize');
session_start();

$_SESSION['ryat'] = $_GET['ryat'];

當第一次訪問該指令碼,並提交資料如下:

foo.php?ryat=|O:8:"stdClass":0:{}

指令碼會按照 php_serialize 處理器的序列化格式儲存資料:

a:1:{s:4:"ryat";s:20:"|O:8:"stdClass":0:{}";}

當第二次訪問該指令碼時,PHP 會按照 php.ini 裡設定的序列化處理器反序列化儲存的資料,這時如果 php.ini 裡設定的是 php 處理器的話,將會反序列化偽造的資料,成功例項化了 stdClass 物件:)

這裡需要注意的是,因為 PHP 自動註冊 Session 會話是在指令碼執行前,所以透過該方式只能注入 PHP 的內建類。

ii)當 session.auto_start=Off 時:

當配置選項 session.auto_start=Off,兩個指令碼註冊 Session 會話時使用的序列化處理器不同,就會出現安全問題,如下面的程式碼:

#!php
//foo1.php

ini_set('session.serialize_handler', 'php_serialize');
session_start();

$_SESSION['ryat'] = $_GET['ryat'];


//foo2.php

ini_set('session.serialize_handler', 'php');
//or session.serialize_handler set to php in php.ini 
session_start();

class ryat {
    var $hi;

    function __wakeup() {
        echo 'hi';
    }
    function __destruct() {
        echo $this->hi;
    }
}

當訪問 foo1.php 時,提交資料如下:

foo1.php?ryat=|O:4:"ryat":1:{s:2:"hi";s:4:"ryat";}

指令碼會按照 php_serialize 處理器的序列化格式儲存資料,訪問 foo2.php 時,則會按照 php 處理器的反序列化格式讀取資料,這時將會反序列化偽造的資料,成功例項化了 ryat 物件,並將會執行類中的 __wakeup 方法和 __destruct 方法:)

iii)其他利用方式?

請自行發掘:)

from:http://www.80vul.com/pch/pch-013.txt

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章