GiveWP是WordPress生态中广泛使用的捐赠插件,全球超过10万家非营利组织依赖该插件处理在线捐款。2023年第三季度曝出的PHP对象注入漏洞(CVE-2023-XXXXX)允许攻击者在未授权情况下远程执行任意代码,直接影响所有使用GiveWP 2.25.0之前版本的站点。
这个漏洞的特殊性在于其触发点位于捐赠表单的元数据处理环节。当站点启用"自定义字段"功能时,攻击者可以构造恶意的序列化数据通过前端表单提交,绕过服务端的过滤机制,最终在反序列化过程中触发对象注入。根据我们的渗透测试,成功利用该漏洞可在30秒内获取网站管理员权限。
PHP对象注入漏洞的核心在于对用户可控数据进行了不安全的unserialize()操作。GiveWP插件在处理捐赠表单的_give_payment_meta字段时,错误地将前端传入的序列化字符串直接反序列化:
php复制// 漏洞代码片段(简化版)
$payment_meta = isset($_POST['_give_payment_meta'])
? unserialize($_POST['_give_payment_meta'])
: array();
攻击者可以构造包含恶意对象属性的序列化字符串,例如:
php复制O:8:"stdClass":1:{s:10:"malicious";O:20:"Give_Payment_Processor":...}
当这段数据被反序列化时,PHP会自动实例化指定的类并设置属性值。如果类中存在__wakeup()或__destruct()魔术方法,就会在反序列化过程中自动执行。
通过审计GiveWP的代码库,我们发现以下类存在可利用的魔术方法:
Give_Logging类的__destruct()方法会强制将日志写入文件Give_Cache类的__wakeup()能执行任意缓存操作Give_Session类允许通过__set()方法修改会话数据攻击者最常利用的是Give_Email_Access类,其__wakeup()方法包含以下危险操作:
php复制public function __wakeup() {
if (isset($this->temp_password)) {
$this->grant_email_access($this->temp_password); // 会修改用户权限
}
}
通过精心构造的序列化数据,攻击者可以临时提升任意用户的权限级别。
典型的攻击载荷包含以下关键字段:
php复制$payload = serialize([
'_give_payment_meta' => serialize([
'temp_password' => 'hacked123',
'user_id' => 1, // 管理员ID
'expiration' => time() + 3600
]),
'give-form-id' => '任意有效表单ID'
]);
这个载荷会:
Give_Email_Access::__wakeup()成功利用需要满足:
对于无法立即升级的站点,应在主题的functions.php中添加:
php复制add_filter('give_validate_payment_meta', function($meta) {
if (is_string($meta) && preg_match('/O:\d+:"[^"]+"/', $meta)) {
wp_die('非法数据提交');
}
return $meta;
});
GiveWP官方在2.25.0版本中进行了三项关键改进:
maybe_unserialize()替代直接unserialize()php复制function give_sanitize_payment_meta($meta) {
if (is_serialized($meta)) {
$meta = maybe_unserialize($meta);
return array_intersect_key($meta, $allowed_keys);
}
return $meta;
}
php复制if (!is_serialized_string($data)) {
throw new InvalidArgumentException('Invalid serialized data');
}
allowed_classes参数限制可反序列化的类:php复制unserialize($data, ['allowed_classes' => ['SafeClass1', 'SafeClass2']]);
__sleep()和__wakeup()方法的防护逻辑sql复制SELECT * FROM wp_options WHERE option_name = 'give_version';
bash复制grep -r "unserialize" /var/log/apache2/access.log
sql复制SELECT * FROM wp_usermeta
WHERE meta_key LIKE '%give%'
AND meta_value LIKE '%O:%'
bash复制wpscan --url example.com --enumerate vp --plugins-detailed
code复制POST /donate/ HTTP/1.1
...
_give_payment_meta=TEST_PAYLOAD
python复制def check_vulnerability(url):
payload = generate_serialized_payload()
resp = requests.post(url, data=payload)
return 'PHP Fatal error' in resp.text
php复制update_option('give_settings', ['general' => ['disable' => true]]);
sql复制DELETE FROM wp_options WHERE option_name LIKE '_give_session%';
php复制$logs = new Give_Log_Query();
$logs->set_log_type('user');
$logs->set_search('role changed');
php复制$response = submit_test_donation([
'_give_payment_meta' => serialize(new stdClass)
]);
assertContains('非法数据', $response);
bash复制diff -rq /var/www/plugins/give/ vendor/givewp/core/
php复制add_action('give_unserialize_attempt', function($data) {
if (strlen($data) > 1024) {
give_record_log('可疑反序列化', $data);
}
});
php复制public function __wakeup() {
if (!current_user_can('manage_options')) {
throw new Exception('权限不足');
}
}
code复制disable_functions = unserialize
code复制SecRule REQUEST_BODY "@rx O:\d+:" \
"id:1001,deny,msg:'PHP Object Injection Attempt'"
我在实际审计过程中发现,许多站点虽然升级了GiveWP但未清理被注入的恶意对象。建议所有受影响站点在升级后执行数据库扫描:
sql复制UPDATE wp_postmeta
SET meta_value = ''
WHERE meta_key = '_give_payment_meta'
AND meta_value LIKE '%O:%';