
Plausible Analytics master d44e104 - 높음 - 원격 - IDOR - PUT traffic-change-notification site_id 대량 할당
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · plausible-traffic-notify
Plausible Analytics 커뮤니티 에디션, GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16) - Plausible Insights OÜ
분류: IDOR (대량 할당). 도달 범위: 원격 (공격자가 이미 제어하는 사이트의 소유자 / 관리자 / 편집자).
Plausible.Site.TrafficChangeNotification.changeset/2는 여전히 :site_id를 캐스팅합니다. SiteController.update_traffic_change_notification/2는 권한이 부여된 경로 도메인으로 행을 로드한 다음, 클라이언트 맵을 Repo.update!합니다. 라우트 PUT /sites/:domain/traffic-change-notification/:type은 :browser + CSRF + AuthorizeSiteAccess [:owner, :admin, :editor, :super_admin] 아래에 위치합니다. HTML 폼은 threshold만 전송합니다. Phoenix는 여전히 추가 키를 허용합니다.
whoareme Bug 02는 Shield / Goal site_id를 같은 방식으로 패치했습니다: socket.assigns.site.id를 바인딩하고 :site_id 캐스팅을 중단합니다. 이 컨트롤러는 해당 패스에 포함되지 않았습니다. 활성화(POST .../enable)는 assigns에서 site_id: site.id를 스탬프합니다. 공격자는 자신의 사이트에서 활성화한 다음 traffic_change_notification[site_id]=<victim_pk>를 PUT합니다. 고유 인덱스 (site_id, type). 기본 설치에는 spike/drop 행이 없으므로 업데이트가 성공하고 해당 행은 이제 피해자 소유가 됩니다. 테이블 이름은 레거시 spike_notifications입니다.
자신의 사이트 편집자가 spike/drop 알림 행을 피해자 사이트로 재귀속시킵니다. 수신자는 공격자 메일박스로 유지됩니다. Oban이 피해자 트래픽에 대해 공격자에게 메일을 보냅니다.
| ID | 아직 CVE 없음 |
| CWE | CWE-915 / CWE-639 |
| CVSS | High: 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L |
| 제품 | Plausible Analytics |
| 영향 받음 | GitHub master d44e104ae3943a9240d5cb6b62e67dff0f069d3a (2026-10-16). 마지막 CE 태그 v3.2.1 (e4f5a87a5570bd61605217e7ceb376636db8eddb, 2026-05-15)은 다섯 달 뒤처져 있습니다. CE + EE (lib/, extra/lib 아님). |
| 인증 | 자신이 제어하는 사이트의 소유자 / 관리자 / 편집자. 뷰어는 불가. CSRF 필요 (동일 세션 IDOR). |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1:18183 (http) 18184 (postgres) 18185 (clickhouse http) 전용 |
Plausible Analytics leftovers의 두 번째 잔여 항목. 동일 핀, 다른 팩: plausible-editor-invite (People HTML이 편집자에게 transfer nanoid를 유출). 이 팩은 여전히 :site_id를 캐스팅하는 Shield/Goal 형제입니다.
이미 제어하는 사이트의 소유자, 관리자 또는 편집자. 해당 세션의 세션 쿠키와 CSRF 토큰이 필요합니다. 피해자에 대한 멤버십은 필요하지 않습니다. 피해자 경로 도메인을 PUT하지 않습니다 (그러면 404가 발생합니다). 자신의 도메인에 외부 site_id 정수 PK를 PUT합니다.
권한 테이블 (전수). Signal-Server Dropwizard admin과 동일한 기준: 메서드 + 경로, 해당 호출이 부여하는 것, 정찰 대 무결성 대 가용성, 랩에서 입증된 것 대 추론된 것, 닫힌 채로 유지되는 인접 표면.
| 축 | 메서드 + 경로 | 부여되는 것 | 출처 |
|---|---|---|---|
| 무결성 | POST /sites/:attacker_domain/traffic-change-notification/:type/enable 후 PUT /sites/:attacker_domain/traffic-change-notification/:type with traffic_change_notification[site_id]=<victim_pk> | 공격자의 spike_notifications 행을 victim.sites.id로 재부모화. 수신자는 공격자 주소로 유지됨. 고유한 (site_id, type) 슬롯을 소유함. 임계값은 공격자가 선택함(랩에서는 1 사용). | 랩에서 입증됨. 행 1의 site_id가 2 → 1로 이동, 수신자 {[email protected]} |
| 정찰 | Oban TrafficChangeNotifier 이메일을 notification.recipients로 발송 | 제목 Traffic spike on <victim.domain>. 본문: 현재 방문자 수, 상위 3개 소스, 상위 3개 페이지. 첫 발송은 즉시(last_sent nil). 최대 12시간마다 반복. 임계값 1이면 피해자에게 현재 방문자가 있을 때마다 메일이 발송됨. 메일 내 대시보드 HTML / 설치 링크는 수신자가 팀/게스트 구성원이 아니면 생략됨(공격자는 아님). | notifier 소스에서 추론됨. 랩에서 메일 미포착(MAILER_ADAPTER=Bamboo.LocalAdapter) |
| 가용성 | 피해자의 동일 유형 POST .../enable | 고유한 (site_id, type)이 거부함. 심어진 행이 사라지기 전까지 피해자는 기능을 켤 수 없음. | 소스 (unique_constraint) |
| 가용성 | 피해자의 disable (site_id 기준 delete_all) | 피해자가 추가 수신자를 발견하면 심어진 행을 지울 수 있음. | 소스 (disable_traffic_change_notification) |
이 핀에서는 닫혀 있음
| 축 | 메서드 + 경로 | 닫혀 있는 이유 |
|---|---|---|
| 무결성 | PUT /sites/:victim_domain/traffic-change-notification/:type | 경로 도메인은 AuthorizeSiteAccess임. 공격자는 구성원이 아님. 랩에서 404 입증됨. attacker-not-member-of-victim=1 |
| 무결성 | Shield / Goal / funnel / segment / shared-link 쓰기 | whoareme Bug 02가 해당 cast :site_id 핸들러들을 패치함. 이들은 assigns를 바인딩함. |
| 정찰 | ClickHouse 덤프, Stats API, 대시보드 HTML | 구성원 아님, 공유 링크 없음. Notifier 메일은 비구성원에게 대시보드 링크를 생략함. |
| 무결성 | tracker ingest | 이미 클라이언트가 선택한 도메인으로 인증 없는 POST /api/event임. 이 잔여물이 아님. |
| 무결성 | 피해자에게 이미 해당 유형이 있을 때 Repo.update! | 고유한 (site_id, type)에서 500. 기본 설치에는 행이 없으므로 첫 심기가 작동함. |
| 무결성 | 공격자 사이트의 viewer | AuthorizeSiteAccess는 owner/admin/editor임. Viewer는 불가. |
| 무결성 | OS 셸, super-admin LiveView | 닫힘. 셸 없음. |
나는 ClickHouse 덤프, 리버스 셸, 또는 피해자로서의 대시보드 로그인을 주장하는 것이 아니다. 덤프는 공격자가 이미 받는 메일 속 트래픽 정찰이다: 도메인, 실시간 방문자, 상위 소스, 상위 페이지. site_id는 정수 PK(열거 가능; whoareme는 site_id=1 사용)이다. 대시보드 data-*는 이를 내보내지 않는다.
whoareme Bug 02는 Shield와 Goal이 :site_id 캐스팅을 중단하도록 가르쳤다. 나는 lib/ 아래 남은 cast(.*:site_id를 grep했다. 트래픽 변경 알림에는 여전히 있었다. Enable은 assigns를 찍는다. Update는 클라이언트 맵을 받는다. 그것이 잔여물이다.
run.sh는 plausible-lab:d44e104ae3943a가 없으면 핀을 클론하고, 두 개의 독립 팀이 등록할 수 있도록 DISABLE_REGISTRATION=false로 compose를 올리고, victim.lab(id 1 team 1)과 attacker.lab(id 2 team 2)을 시드하고, 공격자에서 spike notify를 활성화한 다음 site_id=1로 PUT한다.
이미 기록된 잘못된 길: SECRET_KEY_BASE가 64바이트보다 짧음(Plug.Session.COOKIE가 거부함); 피해자 경로 도메인에 PUT(404, 그것은 닫힌 인접 경로); enable을 버그로 취급(enable은 올바르게 할당됨). 연극.
python3 plausible-traffic-notify-Abraxas-Labs.py
또는
cd lab
./run.sh
오직 http://127.0.0.1:18183만 대상으로 합니다. 이미지 plausible-lab:d44e104ae3943a (MIX_ENV=ce, pin d44e104ae3943a9240d5cb6b62e67dff0f069d3a). Docker가 필요합니다. 첫 실행 시 해당 SHA에서 https://github.com/plausible/analytics를 클론하고 이미지를 빌드합니다. 런처는 lab/로 chdir한 후 run.sh를 exec합니다.
IOC victim.lab id=1 team=1 attacker.lab id=2 team=2
IOC attacker-not-member-of-victim=1
IOC put-victim-path http=404
IOC spike_notifications row 1 site_id 2 -> 1 recipients={[email protected]}
SUCCESS plausible-traffic-notify PLAUSIBLE-H03-TRAFFIC-NOTIFY-SITEID-WITNESS
Witness 문자열: PLAUSIBLE-H03-TRAFFIC-NOTIFY-SITEID-WITNESS. 오라클은 spike_notifications.site_id 이동입니다. Notifier 메일 정찰은 추론된 상태로 유지됩니다.
:site_id 캐스팅을 중단하십시오. whoareme Bug 02 이후 Shield와 Goal이 이미 하는 방식대로 conn.assigns에서 site.id를 바인딩하십시오.
def changeset(schema, attrs) do
schema
|> cast(attrs, [:recipients, :threshold, :type])
|> validate_required([:recipients, :threshold, :type])
|> validate_number(:threshold, greater_than_or_equal_to: 1)
|> unique_constraint([:site_id, :type])
end
enable에서 insert 시 site_id를 assigns에서 유지하세요. PUT body에서 가져오지 마세요.
패치된 빌드에 대해 loopback client를 다시 실행하세요: 외부 site_id로 PUT한 후에도 행은 공격자의 sites.id에 남아 있어야 합니다. 피해자 경로 PUT은 404를 유지해야 합니다.
그때까지는 "Shield와 Goal을 패치했다"를 "Site에 belongs_to하는 모든 changeset이 :site_id 캐스팅을 중단했다"로 간주하지 마세요.