abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · plausible-editor-invite
Plausible Analytics 社区版,GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a(2026-10-16)- Plausible Insights OÜ
类别: 权限残留(邀请令牌泄露)。可达性: 远程(该站点的访客编辑者或团队编辑者)。
Sites.list_people/1 会选取原始的 guest_invitations.invitation_id 和 team_site_transfers.transfer_id。settings_people.html.heex 会把那个 21 字符的 nanoid 写入每个待处理行的 id="invitation-#{invitation.invitation_id}"。人员设置不在 SiteController 的 @special_cased_actions 中,因此 AuthorizeSiteAccess 允许 :editor。访客编辑者映射为站点角色 :editor。访客查看者会返回 404。
Nanoid.generate/0 就是通过邀请注册的持有者。未认证的 GET /register/invitation/:invitation_id 仅按 id 查找。LiveView register 会用 invitation.email 覆盖提交的邮箱。默认 CE 为 DISABLE_REGISTRATION=invite_only 和 ENABLE_EMAIL_VERIFICATION=false。接受操作是 POST /sites/invitations/:id/accept,通过 citext 邮箱匹配。
一个已经能看到人员 HTML 的访客编辑者,读取待处理所有权转移的 nanoid,以被邀请者身份注册,然后接受。站点就转移到攻击者的个人团队上。
| ID | 暂无 CVE |
| CWE | CWE-200 / CWE-269 |
| CVSS | 高:8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L |
| 产品 | Plausible Analytics |
| 受影响版本 | GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a(2026-10-16)。最后一个 CE 标签 v3.2.1(e4f5a87a5570bd61605217e7ceb376636db8eddb,2026-05-15)落后五个月。SECURITY.md 告诉运维人员运行 :latest。固定版本为当前 HEAD。CE MIX_ENV=ce 仅编译 lib/。 |
| 认证 | 已认证的访客编辑者(:guest_member, :editor)或该站点的团队编辑者。非未认证。非访客查看者。 |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1:18180(http)18181(postgres)18182(clickhouse http) |
Plausible Analytics 残留问题 中的残留 #1。相同固定版本,另一个包:plausible-traffic-notify(PUT traffic-change-notification site_id 批量赋值)。本包是窃取待处理所有权转移的那个。
已认证的访客编辑者或站点团队编辑者,该站点存在待处理的访客邀请和/或所有权转移。需要正常的会话 cookie。不需要所有者、管理员或超级管理员 LiveView。他们不会暴力破解 21 字符的 nanoid。他们从被允许加载的 HTML 中读取它,然后以被邀请者身份注册。
权限表(详尽)。与 Signal-Server Dropwizard 管理员 相同的标准:方法 + 路径、该调用授予什么、侦察 vs 完整性 vs 可用性、实验验证 vs 推断、保持关闭的相邻攻击面。
| 维度 | 方法 + 路径 | 授予能力 | 来源 |
|---|---|---|---|
| 侦察 | 以访客/团队编辑身份执行 GET /:domain/settings/people | 待处理行会渲染出 id="invitation-<nanoid>"。Nanoid 即 invitation_id 或 transfer_id。仅限同一站点。被邀请者邮箱也在表格中。 | 实验室验证。 编辑人员页面返回 200,泄露 nanoid Hc8DiBC2q08mv5LFmBysu |
| 侦察 | 未认证 GET /register/invitation/:id | 注册表单上显示被邀请者邮箱。查找仅按 id 进行(find_by_id_unified)。 | 实验室验证。 注册 HTML 显示 [email protected] |
| 完整性 | 在 /register/invitation/:id 上的 LiveView register | 提交的 user[email] 会被 invitation.email 覆盖。在受邀者地址创建新的 CE 账户。默认 CE 邮箱验证关闭。仅限邀请模式仍允许 :invitation。 | 实验室验证。 提交了 [email protected];存储的邮箱为 [email protected] |
| 完整性 | 以该新账户执行 POST /sites/invitations/:id/accept | 待处理的所有权转移会将 sites.team_id 移到 newowner 个人团队。转移行消失。 | 实验室验证。 sites.team_id 1 → 2 |
| 完整性 | 对该邮箱的待处理访客邀请执行同样的接受操作 | 在该站点上获得新成员资格(编辑对编辑不算提权)。该邮箱的其他待处理团队/访客/转移行也会变为可接受。 | 源码(InvitationController.accept_invitation) |
| 可用性 | 转移接受后 | 原团队失去该站点。统计仍在新团队上继续服务。 | 实验室验证。 team_id 已移动 |
在此固定版本上保持关闭
| 维度 | 方法 + 路径 | 为何关闭 |
|---|---|---|
| 侦察 | 访客查看者 GET /:domain/settings/people | 404。查看者不是 :editor。实验室验证。 |
| 完整性 | 编辑对泄露的转移 id 执行 POST /sites/invitations/:id/accept | 邮箱不匹配。编辑不是 [email protected]。站点仍留在团队 1。实验室验证。 |
| 完整性 | 无有效邀请 id 的未认证注册 | check_registration_enabled(:default) 与 :invitation 的对比。whoareme Bug 02,已修补。 |
| 侦察 | 21 字符 nanoid 暴力破解 | URL 安全字母表,21 字符。超出本次遗留问题范围。 |
| 侦察 | ClickHouse 事件、SMTP、其他站点的成员页面 | 仅限同一站点。无 ClickHouse 管理员权限。无邮箱读取。 |
| 完整性 | 所有者/管理员邀请与转移 | 他们本来就可以。本次遗留问题是编辑无法做到。 |
| 完整性 | 超级管理员 LiveView、OS shell、extra/lib EE SSO | 已关闭。CE 实验室。无 shell。 |
我并不声称可以对已注册用户进行未认证账户接管、反向 shell 或 ClickHouse 转储。High 实例是默认 CE 上的待处理所有者转移(或针对该邮箱向其他站点/团队发出的邀请):仅限邀请加上邮箱验证关闭。EE 始终设置 email_verified: false,且 /activate 需要被邀请者邮箱。
whoareme Bug 02(2026-05-22 披露)关闭了无有效邀请的仅限邀请 LiveView 注册,以及 Shield/Goal site_id 批量赋值。我仍然基于当前 master 进行了研究。SECURITY.md 仅修补最新的 MAJOR.MINOR,并告诉运维人员运行 :latest。最后一个 CE 标签 v3.2.1 落后 HEAD 五个月。
我阅读了每一个 nanoid invitation_id 离开数据库的位置。接受和注册将其视为持有者凭证。人员设置将其放入删除按钮的 HTML id= 中。删除仅限所有者/管理员。id= 则不是。编辑会加载该页面。
如果 plausible-lab:d44e104ae3943a 缺失,run.sh 会克隆该固定版本,以 DISABLE_REGISTRATION=invite_only 启动 compose,播种所有者 / 编辑 / 查看者 / 待处理转移,然后比较人员 HTML。
已记录的弯路:将访客注册视为自动接受(需要在登录后执行 POST /sites/invitations/:id/accept);将编辑接受泄露的转移视为窃取(邮箱不匹配,无移动);CE login_ip=5 每分钟导致的登录 429(每个用户保持一个会话);声称查看者可以读取人员页面(404)。纯属表演。
python3 plausible-editor-invite-Abraxas-Labs.py
或
cd lab
./run.sh
仅针对 http://127.0.0.1:18180。镜像 plausible-lab:d44e104ae3943a(MIX_ENV=ce,固定提交 d44e104ae3943a9240d5cb6b62e67dff0f069d3a)。需要 Docker。首次运行会克隆 https://github.com/plausible/analytics 到该 SHA 并构建镜像。启动器会 chdir 进入 lab/ 并执行 run.sh。
IOC editor-people-leak invitation-id=Hc8DiBC2q08mv5LFmBysu
IOC viewer-people http=404
IOC register-email-overwrite [email protected] [email protected]
IOC editor-accept-no-move team_id=1
IOC newowner-accept team_id 1 -> 2
SUCCESS plausible-editor-invite PLAUSIBLE-H02-EDITOR-INVITE-TOKEN-WITNESS
见证字符串:PLAUSIBLE-H02-EDITOR-INVITE-TOKEN-WITNESS。Oracle 是接受后的人员 HTML nanoid 加上 sites.team_id。
不要把 bearer nanoid 放进编辑器可以加载的 HTML 中。人员设置可以用一个不是 invitation_id / transfer_id 的服务端 id 来标识该行,或者为 DOM 对其做哈希。只把真实 token 保留在所有者/管理员的删除表单上。
停止在编辑器视图中于 Sites.list_people/1 里选择原始的 invitation_id / transfer_id。如果编辑器不需要待处理表格,就按 danger-zone 已有的方式,将人员授权为所有者/管理员。
在从邀请注册时,将新账户绑定到一次性哈希 token,而不是同时存在于 CSS id 中的 nanoid。
对修补后的构建重新运行 loopback 客户端:编辑器人员 HTML 不得包含 invitation-<21-char>。Newowner 接受一个仅出现在人员页面上的 token 必须失败。
在此之前,不要把“编辑器无法转移站点”当作“编辑器无法读取转移 bearer”。默认 CE 仅邀请加关闭邮箱验证,会使该 bearer 成为一条注册并接受的路径。