
Plausible Analytics master d44e104 - High - Remote - Privilege leftover - editor people HTML leaks transfer nanoid
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · plausible-editor-invite
Plausible Analytics community edition, GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16) - Plausible Insights OÜ
Class: Privilege leftover (invitation-token leak). Reach: Remote (guest editor or team editor of that site).
Sites.list_people/1 selects raw guest_invitations.invitation_id and team_site_transfers.transfer_id. settings_people.html.heex puts that 21-char nanoid in id="invitation-#{invitation.invitation_id}" for every pending row. People settings is not in SiteController @special_cased_actions, so AuthorizeSiteAccess allows :editor. Guest editor maps to site role :editor. Guest viewer 404s.
Nanoid.generate/0 is the register-from-invite bearer. Unauth GET /register/invitation/:invitation_id looks up by id only. LiveView register overwrites posted email with invitation.email. Default CE is DISABLE_REGISTRATION=invite_only and ENABLE_EMAIL_VERIFICATION=false. Accept is POST /sites/invitations/:id/accept via citext email match.
A guest editor who can already see People HTML reads a pending ownership-transfer nanoid, registers as the invitee, and accepts. The site moves onto the attacker personal team.
| ID | no CVE yet |
| CWE | CWE-200 / CWE-269 |
| CVSS | High: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L |
| Product | Plausible Analytics |
| Affected | GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16). Last CE tag v3.2.1 (e4f5a87a5570bd61605217e7ceb376636db8eddb, 2026-05-15) is five months behind. SECURITY.md tells operators to run :latest. Pin is current HEAD. CE MIX_ENV=ce compiles lib/ only. |
| Auth | authenticated guest editor (:guest_member, :editor) or team editor of that site. Not unauth. Not guest viewer. |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18180 (http) 18181 (postgres) 18182 (clickhouse http) only |
Leftover #1 of Plausible Analytics leftovers. Same pin, other pack: plausible-traffic-notify (PUT traffic-change-notification site_id mass-assignment). This pack is the one that steals a pending ownership transfer.
Authenticated guest editor or team editor of a site that has a pending guest invite and/or ownership transfer. Needs a normal session cookie. No owner, no admin, no super-admin LiveView. They do not brute the 21-char nanoid. They read it from HTML they are allowed to load, then register as the invitee.
Privilege table (exhaustive). Same bar as Signal-Server Dropwizard admin: method + path, what that call grants, recon vs integrity vs availability, lab-proven vs inferred, adjacent surface that stays closed.
| Axis | Method + path | Grants | Provenance |
|---|---|---|---|
| Recon | GET /:domain/settings/people as guest/team editor | Pending rows render id="invitation-<nanoid>". Nanoid is invitation_id or transfer_id. Same site only. Invitee email is also in the table. | Lab-proven. Editor people 200 leaked nanoid Hc8DiBC2q08mv5LFmBysu |
| Recon | unauth GET /register/invitation/:id | Invitee email on the register form. Lookup is by id only (find_by_id_unified). | Lab-proven. Register HTML showed [email protected] |
| Integrity | LiveView register on /register/invitation/:id | Posted user[email] is overwritten with invitation.email. New CE account at the invitee address. Default CE email verification off. Invite-only still allows :invitation. | Lab-proven. Posted [email protected]; stored email was [email protected] |
| Integrity | POST /sites/invitations/:id/accept as that new account | Pending ownership transfer moves sites.team_id onto the newowner personal team. Transfer row is gone. | Lab-proven. sites.team_id 1 → 2 |
| Integrity | same accept on a pending guest invite for that email | New membership on that site (editor-for-editor is not a gain). Other pending team/guest/transfer rows for that email also become accept-able. | Source (InvitationController.accept_invitation) |
| Availability | after transfer accept | Original team loses the site. Stats keep serving on the new team. | Lab-proven. team_id moved |
Stays closed on this pin
| Axis | Method + path | Why it is closed |
|---|---|---|
| Recon | guest viewer GET /:domain/settings/people | 404. Viewer is not :editor. Lab-proven. |
| Integrity | editor POST /sites/invitations/:id/accept of the leaked transfer id | Email match. Editor is not [email protected]. Site stays on team 1. Lab-proven. |
| Integrity | unauth register without a valid invitation id | check_registration_enabled(:default) vs :invitation. whoareme Bug 02, patched. |
| Recon | 21-char nanoid brute | URL-safe alphabet, 21 chars. Out of this leftover. |
| Recon | ClickHouse events, SMTP, other sites' people pages | Same-site only. No ClickHouse admin. No mailbox read. |
| Integrity | owner/admin invite and transfer | They already can. This leftover is the editor who cannot. |
| Integrity | super-admin LiveView, OS shell, extra/lib EE SSO | Closed. CE lab. No shell. |
I am not claiming unauthenticated account takeover of an already-registered user, a reverse shell, or ClickHouse dump. The High instance is a pending owner transfer (or invites to other sites/teams for that email) on default CE: invite-only plus email verification off. EE always sets email_verified: false and /activate needs the invitee mailbox.
whoareme Bug 02 (disclosed 2026-05-22) closed invite-only LiveView register without a valid invite, plus Shield/Goal site_id mass-assignment. I sat on current master anyway. SECURITY.md patches only latest MAJOR.MINOR and tells operators to run :latest. Last CE tag v3.2.1 is five months behind HEAD.
I read every place a nanoid invitation_id leaves the database. Accept and register treat it as a bearer. People settings puts it in an HTML id= for the delete button. Delete is owner/admin only. The id= is not. Editors load the page.
run.sh clones the pin if plausible-lab:d44e104ae3943a is missing, brings compose up with DISABLE_REGISTRATION=invite_only, seeds owner / editor / viewer / pending transfer, then compares people HTML.