前言
php的session信息是儲存在文件中的
session.save_path="" 指定儲存的路徑
session.save_handler="" 指定儲存時使用的函數(默認是file)
session.auto_start boolen
session.serialize_handler="" 定義序列化和反序列化的處理器的名字,默認是php(5.5.4后改為php_serialize)
session.serialize_handler存在以下幾種
- php_binary 鍵名的長度對應的ascii字符+鍵名+經過serialize()函數序列化后的值
- php 鍵名+豎線(|)+經過serialize()函數處理過的值
- php_serialize 經過serialize()函數處理過的值,會將鍵名和值當作一個數組序列化
使用過程中如果想要修改,使用
ini_set('session.serialize_handler','php_serialize');
但這里設置的handler如果和默認的不同,就會出問題
比如默認是php的handler,在該頁面設置為php_serialize
這是如果我們傳入一個 '|O:5:"Class"';,這樣的一個數據,在儲存時就會加上鍵名進行序列化,但是進行讀取的時候還是會按照php handler來處理,以|作為鍵和值的分隔符,將前半部分當作鍵,后半部分當作值,然后進行反序列化
CTF
jarvisoj PHPINFO
引用官方文檔的內容
當 session.upload_progress.enabled INI 選項開啟時,PHP 能夠在每一個文件上傳時監測上傳進度。 這個信息對上傳請求自身並沒有什么幫助,但在文件上傳時應用可以發送一個POST請求到終端(例如通過XHR)來檢查這個狀態
當一個上傳在處理中,同時POST一個與INI中設置的session.upload_progress.name同名變量時,上傳進度可以在$_SESSION中獲得。 當PHP檢測到這種POST請求時,它會在$_SESSION中添加一組數據, 索引是 session.upload_progress.prefix 與 session.upload_progress.name連接在一起的值。 通常這些鍵值可以通過讀取INI設置來獲得
通俗的說就是請求時加上與session.upload_progress.name同名的變量時就會在$_SESSION中加上一組新的數據
來看這道題,題目給了源碼
<?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'));
}
?>
看到這里使用了ini_set('sessionserialize_handler':'php'),有預感會出現handler不一樣的問題
接下來查看phpinfo,發現php版本是5.6.21,大於5.5.4,默認的handler是php_serialize,會出現上面所述的問題
在使用session_start()時會自動加載session文件中的值,因為在這里在__destruct方法中使用eval,所以只要在session文件中寫入這個類,就能夠執行代碼?
但是我們如何將類寫入session文件?
這就用到剛才提到的東西,查看phpinfo,
因為session.upload_progress.enabled=1,所以我們就可以post一個和session.upload_progress.name同名的變量,來使得我們上傳的文件名寫入session
因為這里是php handler,是以|開頭的,所以在反序列化時會按照|來識別鍵值對而不是按照默認的php_serialize來識別session,所以我們將文件名改為
"|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}"
來讀取文件目錄
問題
session部分的問題大致是解決了……剩下的問題就是……為什么要將雙引號轉義?暫時還沒有弄清楚,待日后分析吧