XMPP: snapshot-chunks zijn niet geauthenticeerd — vijandige occupant kan baseline corrumperen (join-DoS) #1411

Closed
opened 2026-08-09 18:22:38 +00:00 by brenno · 0 comments
Owner

Bevinding

lib/xmpp/xmpp_snapshot.darthandleSnapshot (regels 165–202) aanvaardt chunks op basis van een plaintext id-veld (snap-{deviceId}-{counter}) zonder te verifiëren dat de stanza's from overeenkomt met de device-id in dat id. De chunk-metadata (id, i, n, data) is plaintext; er is geen handtekening of afzender-controle op chunk-niveau.

Wat er gebeurt

Een vijandige occupant stuurt een chunk met id: "snap-<authority-deviceId>-0", i: 0, n: 3, data: "garbage". Deze overschrijft chunk 0 van de autoriteit's legitieme snapshot in de receiver's _pending-map. Wanneer chunks 1 en 2 aankomen, is parts.length == count (3), dus de snapshot wordt gereassembleerd. De blob is "garbage" + legit1 + legit2 — misvormde JSON — dus _assemble gooit een uitzondering, die wordt gelogd en gedropt. De _pending-entry voor dat id wordt gewist.

De autoriteit stuurt de baseline niet opnieuw tenzij een newcomer net is gesleuteld (syncNowkeyedDeviceCount > _lastKeyed). Als de newcomer al gesleuteld was toen de corruptie plaatsvond, wordt geen nieuwe baseline gestuurd. De newcomer's firstSnapshot-completer voltooit nooit — zijn sessie start niet.

Trust boundary

MUC (companion room) → Applicatie (snapshot-reassemblage). De MUC reflecteert elke groupchat-message naar elke occupant; een occupant kan stanzas verzenden met een willekeurig id in de chunk-payload.

Impact

Een enkele vijandige occupant kan elke newcomer permanent blokkeren van de baseline, zonder de epoch-sleutel te bezitten. De aanval is onzichtbaar voor de newcomer (geen foutmelding, de sessie start gewoon nooit) en kost de aanvaller één stanza.

Oplossingsrichting

  1. Afzender-controle op chunk-niveau: verifieer dat stanza.from (de in-room JID room@conf/nick) overeenkomt met de device-id in het chunk-id. De chunk-id bevat de afzender's deviceId — vergelijk dit met de device(s) die de directory kent voor dat from-adres. Een chunk van een from die geen device met dat deviceId heeft, wordt fail-closed gedropt.
  2. Aanvullend: overweeg het chunk-id te binden aan de verzegeling — bijv. id = hash(sealed.toContent()) zodat een aanvaller geen geldig id kan construeren zonder de verzegeling te bezitten. Dit maakt ook replay/corruptie onmogelijk.
  3. Herstel: als een snapshot faalt in _assemble, zou de receiver een <resync> moeten kunnen vragen (het transport-pad bestaat al), zodat de autoriteit opnieuw stuurt. Momenteel heeft de snapshot-channel geen pad om een resync te triggeren — alleen het transport (op/lock-gap) doet dat.

Locatie

  • lib/xmpp/xmpp_snapshot.dart regels 165–202 (handleSnapshot), 204–216 (_assemble)
  • lib/xmpp/xmpp_collab_launch.dart regels 167–205 (syncNow — de herzend-logica)

Severity

HIGH — een ongesleutelde occupant kan de hele samenwerking voor newcomers platleggen met één stanza.

## Bevinding `lib/xmpp/xmpp_snapshot.dart` — `handleSnapshot` (regels 165–202) aanvaardt chunks op basis van een plaintext `id`-veld (`snap-{deviceId}-{counter}`) zonder te verifiëren dat de stanza's `from` overeenkomt met de device-id in dat `id`. De chunk-metadata (`id`, `i`, `n`, `data`) is plaintext; er is geen handtekening of afzender-controle op chunk-niveau. ### Wat er gebeurt Een vijandige occupant stuurt een chunk met `id: "snap-<authority-deviceId>-0"`, `i: 0`, `n: 3`, `data: "garbage"`. Deze overschrijft chunk 0 van de autoriteit's legitieme snapshot in de receiver's `_pending`-map. Wanneer chunks 1 en 2 aankomen, is `parts.length == count` (3), dus de snapshot wordt gereassembleerd. De blob is `"garbage" + legit1 + legit2` — misvormde JSON — dus `_assemble` gooit een uitzondering, die wordt gelogd en gedropt. De `_pending`-entry voor dat `id` wordt gewist. De autoriteit stuurt de baseline niet opnieuw tenzij een newcomer net is gesleuteld (`syncNow` → `keyedDeviceCount > _lastKeyed`). Als de newcomer al gesleuteld was toen de corruptie plaatsvond, wordt geen nieuwe baseline gestuurd. De newcomer's `firstSnapshot`-completer voltooit nooit — zijn sessie start niet. ### Trust boundary MUC (companion room) → Applicatie (snapshot-reassemblage). De MUC reflecteert elke groupchat-message naar elke occupant; een occupant kan stanzas verzenden met een willekeurig `id` in de chunk-payload. ### Impact Een enkele vijandige occupant kan elke newcomer permanent blokkeren van de baseline, zonder de epoch-sleutel te bezitten. De aanval is onzichtbaar voor de newcomer (geen foutmelding, de sessie start gewoon nooit) en kost de aanvaller één stanza. ### Oplossingsrichting 1. **Afzender-controle op chunk-niveau:** verifieer dat `stanza.from` (de in-room JID `room@conf/nick`) overeenkomt met de device-id in het chunk-`id`. De chunk-`id` bevat de afzender's deviceId — vergelijk dit met de device(s) die de directory kent voor dat `from`-adres. Een chunk van een `from` die geen device met dat deviceId heeft, wordt fail-closed gedropt. 2. **Aanvullend:** overweeg het chunk-`id` te binden aan de verzegeling — bijv. `id = hash(sealed.toContent())` zodat een aanvaller geen geldig `id` kan construeren zonder de verzegeling te bezitten. Dit maakt ook replay/corruptie onmogelijk. 3. **Herstel:** als een snapshot faalt in `_assemble`, zou de receiver een `<resync>` moeten kunnen vragen (het transport-pad bestaat al), zodat de autoriteit opnieuw stuurt. Momenteel heeft de snapshot-channel geen pad om een resync te triggeren — alleen het transport (op/lock-gap) doet dat. ### Locatie - `lib/xmpp/xmpp_snapshot.dart` regels 165–202 (`handleSnapshot`), 204–216 (`_assemble`) - `lib/xmpp/xmpp_collab_launch.dart` regels 167–205 (`syncNow` — de herzend-logica) ### Severity HIGH — een ongesleutelde occupant kan de hele samenwerking voor newcomers platleggen met één stanza.
brenno 2026-08-09 20:56:22 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#1411
No description provided.