WordPress Bricks(CVE-2024-25600)分析
前言
在玩手机的时候看到了一公众号的推文,标题是WordPress爆炸0day 直接RCE! ,标题有点夸张,我以为是Wordpress
本身的漏洞,点了进去看到是一个名为Bricks Builder插件的未授权RCE漏洞,但是只给出了payload
,看到了payload
于是在网上找分析文章,并没有找到,于是找了源码自己来简单分析下,由于并没有用过这个插件,也没有仔细看大部分源码,因此以下分析不一定十分准确。
关于Bricks Builder
Bricks Builder 是一个 WordPress 页面构建插件,它的主要功能是让用户可以通过直观的界面和拖放操作来创建自定义的网页布局。使用 Bricks Builder,用户可以轻松地设计和定制其网站的页面,而无需编写任何代码。
以下是 Bricks Builder 插件的一些主要功能和特点:
- 拖放构建:Bricks Builder 提供直观的拖放构建界面,让用户可以通过拖放各种元素来建立网页布局,如文本、图像、按钮、分割线等。
- 预设模块:插件提供了各种预设的模块和布局,用户可以直接使用这些模块来快速构建页面,节省时间。
- 响应式设计:Bricks Builder 支持响应式设计,用户可以针对不同设备(如电脑、平板、手机)定制页面布局和样式,确保页面在各种设备上都能正常显示。
- 动画效果:插件提供了一些动画效果和过渡效果,用户可以为页面元素添加动感并提升用户体验。
- 定制样式:用户可以自定义页面的样式,如颜色、字体、间距等,以实现更个性化的设计。
- 快速预览:Bricks Builder 允许用户实时预览页面的变化,以便及时调整和优化页面布局。
漏洞分析
漏洞的最终触发代码是在includes/query.php
的prepare_query_vars_from_settings
函数中,这个函数的功能从settings
变量中获取http
请求的参数,换一句话就是settings
变量存储了一些http
请求的参数,漏洞的触发点是在$user_result = eval( $php_query_raw );
语句中,而这个变量是用户可控的。当settings
数组中useQueryEditor
、ObjectType
属于post,term,user
其中之一,并且queryEditor
不为空的时候,会通过bricks_render_dynamic_data
执行数据渲染操作,其中
$query_vars['queryEditor']
是一个存储动态数据配置的数组,该数组包含了查询参数和条件。
$post_id
是当前页面或发布的 ID,用于指定要渲染动态数据的特定页面或发布。
bricks_render_dynamic_data
函数实际上似乎只是根据queryEditor
是否出现某些内容,做了一些过滤操作,也就是说$php_query_raw
完全可被控制传入到eval
中。
往上寻找,在query.php
的Query
类构造函数能够直接触发prepare_query_vars_from_settings
,需要的条件是进入else
循环中,也就是element
数组中的id
的值为空即可。
继续向上找,找在哪里会实例化Query
类,在ajax.php#render_element
中存在Query
的实例化,需要的条件就是$loop_element
为false
,这里本身赋值为false
,只需要POST
请求中保持没有loopElement
即可保持为false
。
当然这里实现会经过verify_request
的验证,除了验证nonce
的值是否正确歪,通过传入postId
来判断当前用户是否有权限使用页面构建器(builder),如果用户没有权限。
而verify_nonce
方法是用于验证nonce
是否正确,其实也是后面render_element_permissions_check
的检查,因为这里的nonce
是被返回到了前端可见的,因此这个漏洞也称为了未授权RCE
再看这个函数的下面,它会从elements
中获取name
,并且通过name
获取一个类,判断这个类是否存在,如果不存在会抛出doesn't exist
内容,从而导致RCE失败。
在elements.php
中,初始化就定义了很多name
的名称,既然放在了初始化,那么必定就是存在可用的,这也是为什么payload
中要赋值name:container
,其实这里赋值其它比如section
也是可以的。
继续查找render_element
的调用方法,在api.php
中存在命名相同的方法调用了Ajax#render_element
,这里的api.php
实际上是一个处理注册的 REST API 端点之一的文件,
在它的自定义初始化端点函数中可以看到它定义了很多能够注册的REST
路由,API_NAMESPACE
的值就是/bricks/v1/
,通过后面拼接某个字符串的方式触发对应的callback
回调函数以及权限检查,这里的render_element_permissions_check
就是前文所说的nonce
随机数,是能够被看到的,仅仅只检查了随机数的值,而没有检查用户的权限。
因此,整个payload为:
{
"postId":"1",
"nonce":"3d6020fb9a",
"element":{
"name":"container",
"settings":{
"hasLoop":"true",
"query":{
"useQueryEditor":true,
"queryEditor":"system('whoami');throw new Exception();", //throw new Exception()是为了抛错回显
"objectType":"post"
}
}
}
}
还有一个疑问是hasLoop
的作用是什么,在去掉它的时候发现是不可以的,在builder.php
中找到了hasLoop
的说明,是用于表示当前的模块是否启动循环功能的,也可以理解为是否通过循环展示动态数据,可以看作是动态数据渲染的一个开关,因此要设置它为True
。
漏洞验证
fofa语法:body="/wp-content/themes/bricks/"
获取nonce
的值:
直接进行RCE
修复
官方在render_element_permissions_check
中增加了对用户权限的检查,而不只检查nonce
。
修复后的效果:
总结
整个漏洞的产生原因是因为在定义API
端点接口的时候,只对可见的随机数nonce
进行了验证而并没有进行权限检查,导致能够传入数据从而控制动态渲染时$php_query_raw
,最终传入到eval
中,导致了代码的执行。
文章标题:WordPress Bricks(CVE-2024-25600)分析
文章链接:https://aiwin.fun/index.php/archives/4382/
最后编辑:2024 年 2 月 27 日 08:33 By Aiwin
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)