引言
在网络安全领域,技术的研究与讨论是不断进步的动力。本文针对WordPress的一个对象注入漏洞进行分析,旨在分享技术细节并提醒安全的重要性。特别强调:本文内容仅限技术研究,严禁用于非法目的。
漏洞背景
继WordPress CVE-2022-21661注入漏洞之后,安全研究人员又发现了一个严重的对象注入漏洞,该漏洞允许具有管理员权限的用户通过修改特定的选项来获取服务器的shell访问权限。
影响范围
以下是受该漏洞影响的WordPress实例条件:
- WordPress版本低于5.8.3。
- 启用了多站点模式。
- 存在一条可用的反序列化链(本文不展开讨论)。
GitHub上的漏洞修复记录可以在此链接查看:
环境搭建
为了复现此漏洞,首先需要搭建一个符合条件的WordPress环境。可以通过以下命令获取漏洞修复前的WordPress源码:
git clone https://github.com/WordPress/WordPress
git checkout 7d20ea9
漏洞分析
漏洞入口点
首先来到 wp-admin/upgrade.php
,这是管理员可以直接访问到的文件,也是我认为的此漏洞的入口点。
跟进 wp_upgrade
函数,来到 wp-admin/includes/upgrade.php
首先看几个全局变量,$wp_db_version
在 wp-includes/version.php
中定义,根据其定义处的注释来看,这是本安装程序的数据库版本,在下一行中从数据库中取出了 db_version
赋值给 $wp_current_db_version
,看这情况,大概是一个是固定的数据库版本,另一个是可变的,upgrade.php
会时不时地比较,当发生改变时,会进行一些操作
接下来看到我下的断点处,进入 upgrade_all()
函数
这里就是在将两个版本进行比较,一致时不发生任何变化,不一致时运行后面的代码,接着看下面
根据从数据库中取出的 db_version
也就是此处的 $wp_current_db_version
的大小,会进行不同的处理,我们来关注断点处的 upgrade_280
函数
看到 1611 行的 is_multisite()
,跟进
这是一个判断是否开启了多站点的函数,这也是本漏洞的一个开启条件,必须要开启多站点才可以。开多站点需要改配置文件,我这里图省事儿,直接改了判断条件 !is_multisite()
,强行让他绕过了
继续看上面,进入循环,每次从 wp_options
表中取 20 条数据,将每一条数据都进行反序列化,漏洞的点就在此处了,因为 wp_options
中的数据大部分都是可控的,我们可以通过管理员修改其值,最后进入反序列化。
还有两个问题,第一,如何控制 db_version
的值,让他进入我们想要进入的函数;第二,如何更改 wp_options
中的值。
数据写入
其实这两个是同一个问题,一并解决,wordpress
后台没有直接访问所有 options
的按钮,但我们可以访问 wp-admin/options.php
这里可以更改 db_options
中大部分的值,包括 db_version
,我们将其修改为 10300,就满足了进入漏洞函数的要求,接下来我们尝试写入反序列化字符串
我们先随便选择一个选项写入反序列化字符串,这里我首先选择的是 blogdescription
,也就是博客描述,这个也可以在常规选项中更改(更建议,因为需要处理的值会更少,调试没那么费力),post
的数据会逐个进入 wp-includes/option.php
中的 update_option
,来看几个比较重要的函数(PS:太长截图截不完)
这里首先说明一下 $option
是每个选项的键,$value
是每个选项的值
首先看到 sanitize_option
这个函数会根据不同的键来选择不同的处理方式,比如一些一定会用整数的,就会intval
处理,所以选择的选项也是有讲究的,我之前选择的blogdescription
,就因为这个函数而无法使用,他会将一些特殊字符编码,导致无法正常反序列化,这个可以慢慢尝试,尽量选择这个函数不会进行太多处理的选项
最终我选择的是 wp-admin/options-writing.php
中的 “密码”(选择 options.php
中的mailserver_pass
也一样,是同一个),一般来说,密码对字符都不会有太多的限制,至少这里是的
回到之前的 update_option
的代码,稍微注意一下这里的比较
如果我们输入的选择没有发生改变,就不会继续后面的代码,继续往下走,进入 wp-includes/functions.php
中的maybe_serialize
函数
如果是数组或者对象,那么会直接序列化后返回。然后进入 is_serialized
函数
这里会取字符串的第一个字符进行比较,这里几乎囊括了反序列化字符串的所有类型,遇到是这一些的,返回后会再次反序列化,也就是进行了二次反序列化,这样几乎是防止了可能的反序列化。
这里遗漏了一个 C
类型,官方文档对他的描述是 custom object
,自定义对象,这个以前几乎没有用到过,因此这次进行了一次测试
代码:
结果
同一个类,将类型从 O
修改为了 C
,反序列化运行后,虽然报错,但最后仍然会触发 __destruct
方法,因此我们可以将一个正常的反序列化字符串,将第一个字符 O
修改为 C
,这样就可以逃过这里的二次反序列化,从而将我们的反序列化字符串写入数据库,等待触发即可。
0x03 漏洞复现
本菜鸡找不到 wordpress
的链子,只能随意触发一个 __destruct
意思意思
这里使用 wp-includes/Requests/Transport/cURL.php
中的 Requests_Transport_cURL
类
C:23:"Requests_Transport_cURL":0:{}
查看数据库,并没有被二次序列化
来到wp-admin/options.php
,将 db_version
修改为 10300
这里一个意外的发现,修改数据库的时候就会触发漏洞入口
成功取出数据库中的反序列化串,并且执行反序列化
触发 __destruct
方法
总结
通过对WordPress对象注入漏洞的深入分析与复现,我们发现漏洞的利用存在一定的复杂性。这不仅是对技术能力的挑战,也是对安全意识的考验。在学习和研究过程中,我们应始终保持谨慎和敬畏之心。
参考链接
- GitHub漏洞利用代码仓库:d5shenwu/vulPOC
关于
剑芸信息安全团队
剑芸信息安全团队成立于2022年9月,我们是一个专注于互联网攻防技术研究的团队。我们的研究领域涵盖网络攻防、Web安全、移动终端安全、安全开发以及IoT/物联网/工控安全等。
想了解更多关于我们,请继续关注。