PHP 內置了多種處理器用于存取 $_SESSION 數據時會對數據進行序列化和反序列化,常用的有以下三種,對應三種不同的處理格式:
處理器 | 對應的存儲格式 |
---|---|
php | 鍵名 + 豎線 + 經過 serialize() 函數反序列處理的值 |
php_binary | 鍵名的長度對應的 ASCII 字符 + 鍵名 + 經過 serialize() 函數反序列處理的值 |
php_serialize (php>=5.5.4) |
經過 serialize() 函數反序列處理的數組 |
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 對象:)
當配置選項 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 的內置類。
當配置選項 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 方法:)
請自行發掘:)
from:http://www.80vul.com/pch/pch-013.txt