CVE-2024-5932/CVE-2024-8353 wordpress givewp漏洞分析
前言
CVE-2024-8353相对于CVE-2024-5932,多了部分绕过的样子,核心利用还是不变的。这个漏洞是个人开发工具的时候作为样本测试分析的,因此没有关注里面的绕过内容。为什么用CVE-2024-8353呢,因为在使用CVE-2024-5932的poc的时候发现无法成功,即使我版本啥的都没问题也还是没成,后续用CVE-2024-8353能够触发payload,因此用它来分析了。
那么正式开始正文,总的来说,在默认设置下,捐款的时候新用户(新的邮箱)会收到一份邮件,该邮件模板中存在{name},会到达我们的漏洞函数中触发toString方法从而完成pop链
业务流程分析
- 创建 捐款表单
- 虽然各种类型表单都行,我们payload中修改表单id号都能用,但是第二个classic form发送到请求包才是我们要的格式
- 默认设置就行,添加名字,然后直接发布
- 回到all forms页面,我们能发现已经有我们刚创建的表单了
- 创建新的文章,添加该表单
成功
6.捐100,填好以下内容,然后发送请求包(名字不能有数字)
所以我们入口函数从 give_process_donation_form() 开始
- 在进入正式的代码跟踪前,先说明到底发生了什么。
根据描述,发送给新的,使用offline donation的用户(新邮箱)。
除此外我们也能在form表单中的设置中查看到,可以选择全局定义的邮件,就是上图。或是自定义的邮件。
默认使用全局。
点进去该New Offline Donation发现其邮件内容是使用了{name}标签,在处理该标签的函数中存在我们的漏洞点,触发title属性的toString方法
代码调用链
多图杀猫警告。
首先入口点
经过以下一系列变换到达我们函数
接下来除了必要说明,会一直贴图顺序调用
通过debug直到进入了give_gateway_offline,这大概是请求包这里自定义的(还记得我们之前业务发送请求包的时候选择的支付方式是offline吗)
查询对应函数
进入对应函数的42到44行,发现是匿名函数的调用
我们从这里继续
其内部实际上也还是执行do_action,如下图
查询对应函数
在 PHP 中,__invoke
是一个魔术方法,当试图将对象当作函数来调用时会被触发。
上面在new了对象后,调用了该对象的__invoke函数
我们可以观察上图传入该函数的content内容
preg_replace_callback是php的方法,简单说就是查询类似{name} {siteurl}等大括号tag内容,然后执行本对象的do_tag方法
这部分就是对content中每个tag如{site_url}进行识别,然后分配到对应的执行函数中。
通过{name} 的tag,我们进入下一个函数
可以查看该参数内容,title就是我们注入点
如果进去give_get_payment_meta_user_info()内部的话,会发现它实际上是根据payment_id提取数据,然后进行了反序列化(maybe_unserialize是wordpress的检查并反序列化函数)。
回到刚才继续进入到give_get_email_names中
最终到达上图的漏洞点,title的反序列化对象能触发tostring方法。
利用链
说实话让我构建我肯定构建不出来,而且这也不是我的重点,所以我就把分析过程简单放在下面了。
payload如下
1 | \O:19:"Stripe\\\\StripeObject":1:{s:10:"\0*\0_values";a:1:{s:3:"foo";O:62:"Give\\\\PaymentGateways\\\\DataTransferObjects\\\\GiveInsertPaymentData":1:{s:8:"userInfo";a:1:{s:7:"address";O:4:"Give":1:{s:12:"\0*\0container";O:33:"Give\\\\Vendors\\\\Faker\\\\ValidGenerator":3:{s:12:"\0*\0validator";s:10:"shell_exec";s:12:"\0*\0generator";O:34:"Give\\\\Onboarding\\\\SettingsRepository":1:{s:11:"\0*\0settings";a:1:{s:8:"address1";s:4:"calc";}}s:13:"\0*\0maxRetries";i:10;}}}}}} |
直接从StripeObject类的__tostring方法开始
获取字典_values中每个key对应的value
根据payload内容直到,我们是只有一个userInfo属性的GiveInsertPaymentData对象
继续
userInfo['address'] 中的内容是一个类对象Give,上图中赋值语句触发了Give对象的_get方法
上图中触发container对象ValidGenerator的get方法,但实际上并不存在,因此触发该对象的__call方法,即我们最终调用函数的方法
我们可以观察方法传入了什么参数
然后再看看执行参数分别是什么内容



payload中我们知道generator中的对象是 SettingsRepository,他确实有个对象get
简单说,执行settingsRepository的get()函数,获取了address1中的字符串calc
最后根据maxRetries,调用了maxRetries次数的最终执行 shell_exec('calc')
参考链接
- https://xz.aliyun.com/t/15699?time__1311=GqjxnieDqYqmqGNPeeqBK0QG8W7vTwh3EbD
- https://github.com/EQSTLab/CVE-2024-8353/tree/main