摘要
CVE-2026-75816 涉及 DynamiApps 的 WordPress 插件 Frontend Admin by DynamiApps(插件标识为 acf-frontend-form-element)。在满足特定配置条件的公开前端表单中,未登录访问者能够让用户邮箱字段沿文章保存流程处理,绕过目标用户的编辑权限检查,将所选账户的邮箱改为攻击者控制的地址。报告中的运行时证据支持对管理员邮箱的未授权修改;通过替换邮箱进一步接管账户则仍取决于密码重置邮件能否成功送达。
据原报告转述的 NVD 记录,报告来源为 Wordfence,漏洞评级为 Critical,CVSS 评分为 9.8,受影响版本范围为 ≤ 3.29.12,修复版本为 3.29.13。这里沿用报告提供的评级与评分,未独立计算评分。本文仅依据所给材料整理,未独立复现漏洞或核验外部记录。
漏洞根因
问题在于表单可见性、记录类型与编辑权限之间缺少一致的约束。PermissionsTab::conditions_logic() 在 who_can_see 为 all 时允许表单展示,并应用 frontend_admin/special_permissions 过滤器。但公开展示表单并不意味着访问者有权修改该表单指向的用户账户。
当表单配置为从 URL 查询参数取得编辑目标时,get_form_fields() 对该值使用 sanitize_text_field(),却没有要求它必须是数值标识。因此,带 user_ 前缀的 ACF 用户记录标识也能成为表单的 post_id。该值随后进入加密的 _acf_objects 隐藏字段,再由 get_form_data() 恢复到 post 记录中。隐藏字段经过加密,并不能弥补生成这些元数据时缺失的目标授权检查。
记录类型的混用使后续权限判断失效。ActionPost::conditions_logic() 遇到非数值 post_id 时提前返回,未执行后面的 current_user_can(‘edit_post’, $post_id) 检查。与此同时,create_record() 信任提交的 acff 分组名称;把 user_email 字段置于 post 分组,会使其进入 ActionPost::run(),绕开包含 current_user_can(‘edit_user’) 检查的 ActionUser 流程。
最终,ActionPost::save_post() 将非核心字段交给 acf_update_value(),由 acf/pre_update_value/type=user_email 过滤器触发用户邮箱字段的 pre_update_value()。该方法从 user_ 记录前缀中提取用户标识,再调用 wp_update_user()。在报告分析的 3.29.11 代码中,这一邮箱更新位置没有能力或归属检查,因而能够把文章操作流程中的用户记录目标转化为实际的账户邮箱修改。
提交入口同样不能提供所缺失的授权保障:frontend_admin/form_submit 同时注册了 wp_ajax_ 和 wp_ajax_nopriv_ 处理,允许未登录请求到达。check_submit_form() 验证的 nonce 可从公开表单页面取得;拥有有效 nonce 只能通过这项提交校验,不能证明访问者有权编辑目标用户。
攻击链与前置条件
这条攻击链要求插件已安装并启用,且站点已经配置了未登录访客可见的前端表单:who_can_see 为 all,表单包含 user_email 字段,并允许编辑目标来自 URL 的 post_id 参数或其他攻击者可影响的 post_id 记录值。报告描述的配置使用 post_to_edit=url_query。公开表单的管理端创建属于预先存在的站点配置条件,与攻击请求本身分开;攻击阶段不需要登录账户、登录 Cookie 或受害者交互。材料未评估此类配置在真实站点中的普及程度,不能据此认定所有安装该插件的站点均暴露于这条攻击链。
报告描述的流程先通过公开表单取得指向所选用户记录的 nonce 和隐藏元数据,再向 /wp-admin/admin-ajax.php 的 frontend_admin/form_submit 动作提交数据。邮箱字段被放入 acff[post] 分组,使系统在保留用户记录目标的同时沿文章操作路径执行。非数值标识绕过 ActionPost 的授权检查后,用户邮箱更新钩子将替换地址写入所选账户。
邮箱替换后,攻击者可能通过 /wp-login.php?action=lostpassword 发起 WordPress 原生密码重置,以尝试完成账户接管。不过,报告只观察到重置流程完成账户查找并尝试发送邮件,实际投递失败,失败原因也未调查。重置邮件的接收和最终密码修改均未演示,因此已证实的结果应表述为未授权邮箱修改,而完整账户接管仍是有条件的后续影响。
影响范围
直接影响是未登录者能够修改任意目标用户账户的邮箱,包括管理员账户,破坏账户资料的完整性。运行时证据仅覆盖 3.29.11,并支持管理员邮箱已被修改;这一结果不能直接推广为整个通告范围内各版本均已完成运行时验证。材料未展示远程代码执行或服务崩溃。
版本范围与修复分析需要同时保留。报告引用的通告将 ≤ 3.29.12 列为受影响范围,但其对官方发布归档的源码比较指出,3.29.12 已在 user_email 更新钩子中增加 current_user_can(‘edit_user’, $user_id) 检查。按该源码分析,这项部分修复能够阻断所述未登录攻击链,而非数值 post_id 导致 ActionPost 提前跳过授权的结构性问题仍然存在。由此既不能把 3.29.11 的实测结果直接套用于 3.29.12,也不能擅自改写报告引用的受影响版本范围。
报告将 3.29.13 确认为修复版本。其源码比较显示,该版本把 ActionPost 中合法的非数值状态限制为 none 和 add_post;其他非数值值会进入授权检查并被重置为 none。报告还记录了类似的 term 操作修复、display.php::ajax_add_form() 的数值标识处理,以及 submit.php 中 Role 字段的归属检查。这些结论来自发布归档的源码比较,并非对 3.29.12 或 3.29.13 的运行时验证。由于访问验证阻碍了 Wordfence 公告页和 WordPress Trac 变更页的读取,报告未取得原始修复说明。
检测与处置
应升级到报告列出的修复版本 3.29.13。排查暴露面时,重点检查未登录者可访问的表单是否包含 user_email 字段,以及编辑目标是否受查询参数或其他外部记录值影响。无法立即升级时,可依据这些前置条件临时停用相关公开表单、限制其未登录访问,或移除其中的邮箱编辑能力及外部可控编辑目标,以减少所述路径的暴露;这些配置措施不能替代版本修复。
请求排查应关联两个阶段:公开表单访问中出现带 user_ 前缀的 post_id,随后向 /wp-admin/admin-ajax.php 提交 frontend_admin/form_submit 请求,且 user_email 字段位于 acff[post] 分组。应结合可用的请求记录核查这些字段及其先后关系,不能仅凭访问了通用 AJAX 入口就判定发生攻击。
报告还指出,3.29.11 的 [FEA_PREVIEW_DEBUG] 日志可以暴露记录结构。若已有相关日志,可查找 post 目标带 user_ 前缀、同时 fields.post 下含有 user_email 的记录,用于印证用户字段被送入文章处理流程。发现此类线索后,应核查目标账户是否发生未经授权的邮箱变更,尤其关注管理员账户。
报告中的提交返回了成功的“Post updated”响应,并通过公开 /wp-json/wp/v2/users/ 接口中目标用户 avatar_urls 的变化,另行印证了邮箱修改。该字段中观察到的是邮箱派生的 SHA-256 标识,也使未登录访问者能够获得这类邮箱派生标识。提交成功响应与相关标识变化可作为关联证据,但不能单独证明密码重置已完成或账户已被接管。处置记录应明确区分请求线索、已确认的邮箱变更与后续账户控制结果。