
Plausible Analytics master d44e104 - Alta - Remoto - IDOR - PUT traffic-change-notification site_id mass assignment
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · plausible-traffic-notify
Plausible Analytics community edition, GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16) - Plausible Insights OÜ
Classe: IDOR (mass-assignment). Portata: Remota (owner / admin / editor di un sito già controllato dall'attaccante).
Plausible.Site.TrafficChangeNotification.changeset/2 esegue ancora il cast di :site_id. SiteController.update_traffic_change_notification/2 carica la riga tramite il dominio del percorso autorizzato, poi esegue Repo.update! della mappa del client. La route PUT /sites/:domain/traffic-change-notification/:type si trova sotto :browser + CSRF + AuthorizeSiteAccess [:owner, :admin, :editor, :super_admin]. Il form HTML invia solo threshold. Phoenix accetta comunque chiavi aggiuntive.
whoareme Bug 02 ha corretto Shield / Goal site_id allo stesso modo: associare socket.assigns.site.id, smettere di eseguire il cast di :site_id. Questo controller non era incluso in quella passata. L'abilitazione (POST .../enable) imposta site_id: site.id dagli assigns. L'attaccante abilita sul proprio sito, poi invia una PUT con traffic_change_notification[site_id]=<victim_pk>. Indice univoco (site_id, type). L'installazione predefinita non ha righe spike/drop, quindi l'update riesce e la riga ora appartiene alla vittima. Il nome della tabella è legacy: spike_notifications.
Un editor del proprio sito riassegna la riga di notifica spike/drop a un sito vittima. I destinatari restano la casella dell'attaccante. Oban invia all'attaccante email sul traffico della vittima.
| ID | nessuna CVE al momento |
| CWE | CWE-915 / CWE-639 |
| CVSS | Alto: 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L |
| Prodotto | Plausible Analytics |
| Affetto | GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16). L'ultimo tag CE v3.2.1 (e4f5a87a5570bd61605217e7ceb376636db8eddb, 2026-05-15) è indietro di cinque mesi. CE + EE (lib/, non extra/lib). |
| Auth | owner / admin / editor di un sito che controllano. Viewer non può. CSRF richiesto (IDOR nella stessa sessione). |
| Licenza | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18183 (http) 18184 (postgres) 18185 (clickhouse http) soltanto |
Residuo #2 di Plausible Analytics leftovers. Stesso pin, altro pacchetto: plausible-editor-invite (l'HTML di People espone il nanoid di trasferimento agli editor). Questo pacchetto è il fratello Shield/Goal che esegue ancora il cast di :site_id.
Owner, admin o editor di qualsiasi sito che già controllano. Serve un cookie di sessione e il token CSRF di quella sessione. Non serve l'appartenenza al sito vittima. Non inviano una PUT sul dominio del percorso della vittima (restituirebbe 404). Inviano una PUT sul proprio dominio con una PK intera site_id estranea.
Tabella dei privilegi (esaustiva). Stessa soglia di Signal-Server Dropwizard admin: metodo + percorso, cosa concede quella chiamata, recon vs integrità vs disponibilità, dimostrato in lab vs dedotto, superficie adiacente che resta chiusa.
| Asse | Metodo + percorso | Concede | Provenienza |
|---|---|---|---|
| Integrità | POST /sites/:attacker_domain/traffic-change-notification/:type/enable poi PUT /sites/:attacker_domain/traffic-change-notification/:type con traffic_change_notification[site_id]=<victim_pk> | Ri-assegna la riga spike_notifications dell'attaccante su victim.sites.id. I destinatari restano l'indirizzo dell'attaccante. Possiede lo slot univoco (site_id, type). La soglia è scelta dall'attaccante (il lab ha usato 1). | Dimostrato in lab. La riga 1 ha spostato site_id da 2 → 1, destinatari {[email protected]} |
| Ricognizione | Email Oban TrafficChangeNotifier a notification.recipients | Oggetto Traffic spike on <victim.domain>. Corpo: conteggio visitatori attuale, primi 3 sorgenti, prime 3 pagine. Il primo invio è immediato (last_sent nil). Ripetizione al massimo ogni 12 ore. La soglia 1 produce una mail ogni volta che la vittima ha un qualsiasi visitatore attuale. L'HTML della dashboard / i link di installazione nella mail sono omessi a meno che il destinatario non sia un membro del team/guest (l'attaccante non lo è). | Inferito dal sorgente del notifier. Mail non catturata in lab (MAILER_ADAPTER=Bamboo.LocalAdapter) |
| Disponibilità | POST .../enable della vittima dello stesso tipo | Lo slot univoco (site_id, type) rifiuta. La vittima non può attivare la funzione finché la riga impiantata non viene rimossa. | Sorgente (unique_constraint) |
| Disponibilità | disable della vittima (delete_all per site_id) | La vittima può eliminare la riga impiantata se nota il destinatario aggiuntivo. | Sorgente (disable_traffic_change_notification) |
Resta chiuso su questo pin
| Asse | Metodo + percorso | Perché è chiuso |
|---|---|---|
| Integrità | PUT /sites/:victim_domain/traffic-change-notification/:type | Il dominio del percorso è AuthorizeSiteAccess. L'attaccante non è un membro. Dimostrato in lab 404. attacker-not-member-of-victim=1 |
| Integrità | Scrittura Shield / Goal / funnel / segment / shared-link | whoareme Bug 02 ha corretto quei gestori cast :site_id. Essi legano gli assigns. |
| Ricognizione | Dump ClickHouse, Stats API, HTML della dashboard | Nessuna membership, nessun link condiviso. La mail del notifier omette i link della dashboard per un non-membro. |
| Integrità | ingest del tracker | Già POST /api/event non autenticato con un dominio scelto dal client. Non questo residuo. |
| Integrità | Repo.update! quando la vittima ha già quel tipo | 500 sull'univoco (site_id, type). L'installazione predefinita non ha righe, quindi il primo impianto funziona. |
| Integrità | viewer del sito dell'attaccante | AuthorizeSiteAccess è owner/admin/editor. Il viewer non può. |
| Integrità | shell del sistema operativo, LiveView super-admin | Chiuso. Nessuna shell. |
Non sto rivendicando un dump ClickHouse, una reverse shell o un login alla dashboard come vittima. Il dump è ricognizione del traffico nella mail che l'attaccante già riceve: dominio, visitatori live, principali sorgenti, principali pagine. site_id è la PK intera (enumerabile; whoareme ha usato site_id=1). I data-* della dashboard non lo emettono.