Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
plausible-traffic-notify — Plausible Analytics master d44e104 - High - Remote - IDOR - PUT traffic-change-notification site_id mass assignment | Kitploit
Tools/GitHubGitHub/abraxas/plausible-traffic-notify
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingLabs & Practice
GitHubabraxas/plausible-traffic-notify

plausible-traffic-notify

Plausible Analytics master d44e104 - High - Remote - IDOR - PUT traffic-change-notification site_id mass assignment

View Repository
13 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - plausible-traffic-notify

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  plausible-traffic-notify

plausible-traffic-notify

Plausible Analytics community edition, GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16) - Plausible Insights OÜ

Class: IDOR (mass-assignment). Reach: Remote (owner / admin / editor of a site the attacker already controls).

Plausible.Site.TrafficChangeNotification.changeset/2 still casts :site_id. SiteController.update_traffic_change_notification/2 loads the row by the authorized path domain, then Repo.update! of the client map. Route PUT /sites/:domain/traffic-change-notification/:type sits under :browser + CSRF + AuthorizeSiteAccess [:owner, :admin, :editor, :super_admin]. The HTML form only posts threshold. Phoenix still accepts extra keys.

whoareme Bug 02 patched Shield / Goal site_id the same way: bind socket.assigns.site.id, stop casting :site_id. This controller was not in that pass. Enable (POST .../enable) stamps site_id: site.id from assigns. Attacker enables on their own site, then PUTs traffic_change_notification[site_id]=<victim_pk>. Unique index (site_id, type). Default install has no spike/drop row, so the update succeeds and the row now belongs to the victim. Table name is legacy spike_notifications.

An editor of their own site re-parents the spike/drop notify row onto a victim site. Recipients stay the attacker mailbox. Oban mails attacker about victim traffic.

IDno CVE yet
CWECWE-915 / CWE-639
CVSSHigh: 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L
ProductPlausible Analytics
AffectedGitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16). Last CE tag v3.2.1 (e4f5a87a5570bd61605217e7ceb376636db8eddb, 2026-05-15) is five months behind. CE + EE (lib/, not extra/lib).
Authowner / admin / editor of a site they control. Viewer cannot. CSRF required (same-session IDOR).
LicenseGNU Affero GPL v3.0
Lab127.0.0.1:18183 (http) 18184 (postgres) 18185 (clickhouse http) only

Leftover #2 of Plausible Analytics leftovers. Same pin, other pack: plausible-editor-invite (People HTML leaks transfer nanoid to editors). This pack is the Shield/Goal sibling that still casts :site_id.

What an attacker can do

Owner, admin, or editor of any site they already control. Needs a session cookie and the CSRF token from that session. They do not need membership on the victim. They do not PUT the victim path domain (that 404s). They PUT their own domain with a foreign site_id integer PK.

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.

AxisMethod + pathGrantsProvenance
IntegrityPOST /sites/:attacker_domain/traffic-change-notification/:type/enable then PUT /sites/:attacker_domain/traffic-change-notification/:type with traffic_change_notification[site_id]=<victim_pk>Re-parent attacker spike_notifications row onto victim.sites.id. Recipients stay the attacker address. Owns the unique (site_id, type) slot. Threshold is attacker-chosen (lab used 1).Lab-proven. Row 1 moved site_id 2 → 1, recipients {[email protected]}
ReconOban TrafficChangeNotifier email to notification.recipientsSubject Traffic spike on <victim.domain>. Body: current visitor count, top 3 sources, top 3 pages. First send is immediate (last_sent nil). Repeat at most every 12 hours. Threshold 1 yields a mail whenever the victim has any current visitor. Dashboard HTML / installation links in the mail are omitted unless the recipient is a team/guest member (attacker is not).Inferred from notifier source. Mail not captured in lab (MAILER_ADAPTER=Bamboo.LocalAdapter)
Availabilityvictim POST .../enable of the same typeUnique (site_id, type) rejects. Victim cannot turn the feature on until the planted row is gone.Source (unique_constraint)
Availabilityvictim disable (delete_all by site_id)Victim can clear the planted row if they notice the extra recipient.Source (disable_traffic_change_notification)

Stays closed on this pin

AxisMethod + pathWhy it is closed
IntegrityPUT /sites/:victim_domain/traffic-change-notification/:typePath domain is AuthorizeSiteAccess. Attacker is not a member. Lab-proven 404. attacker-not-member-of-victim=1
IntegrityShield / Goal / funnel / segment / shared-link writewhoareme Bug 02 patched those cast :site_id handlers. They bind assigns.
ReconClickHouse dump, Stats API, dashboard HTMLNo membership, no shared link. Notifier mail omits dashboard links for a non-member.
Integritytracker ingestAlready unauth POST /api/event with a client-chosen domain. Not this leftover.
IntegrityRepo.update! when victim already has that type500 on unique (site_id, type). Default install has no row, so first plant works.
Integrityviewer of attacker siteAuthorizeSiteAccess is owner/admin/editor. Viewer cannot.
IntegrityOS shell, super-admin LiveViewClosed. No shell.

I am not claiming ClickHouse dump, a reverse shell, or dashboard login as the victim. The dump is traffic recon in mail the attacker already receives: domain, live visitors, top sources, top pages. site_id is the integer PK (enumerable; whoareme used site_id=1). Dashboard data-* does not emit it.

How I found it

whoareme Bug 02 taught Shield and Goal to stop casting :site_id. I grepped remaining cast(.*:site_id under lib/. Traffic-change notification still had it. Enable stamps assigns. Update takes the client map. That is the leftover.

run.sh clones the pin if plausible-lab:d44e104ae3943a is missing, brings compose up with DISABLE_REGISTRATION=false so two independent teams can register, seeds victim.lab (id 1 team 1) and attacker.lab (id 2 team 2), enables spike notify on attacker, then PUTs site_id=1.

Wrong turns already recorded: SECRET_KEY_BASE shorter than 64 bytes (Plug.Session.COOKIE refuses it); PUT on the victim path domain (404, that is the closed adjacent); treating enable as the bug (enable is correctly assigned). Theatre.

Lab

python3 plausible-traffic-notify-Abraxas-Labs.py

or

cd lab
./run.sh
Download Tool