Skip to content

CVE‐2026‐71890

David Hook edited this page Oct 2, 2026 · 1 revision

Title: MLS external commit can remove an arbitrary group member.

Issue affecting: BC before 1.86 (from 1.73).

Fixed versions: BC 1.86.

Platform affected: Java 8 and later.

RFC 9420's External Commit mechanism lets a client join a group using only the group's public GroupInfo, which applications are expected to make available for exactly that purpose. Sec. 12.2 allows such a commit to carry "at most one Remove proposal, with which the joiner removes an old version of themselves", and requires that where one is present "the LeafNode in the path field of the external Commit MUST meet the same criteria as would the LeafNode in an Update for the removed leaf … the credential in the LeafNode MUST present a set of identifiers that is acceptable to the application for the removed participant." That is the "resync" flavour of external commit — replacing your own stale leaf.

Group.validateExternalCachedProposals checked the shape of the proposal list (exactly one ExternalInit, at most one Remove, nothing disallowed) and bounded the removed leaf index, but never compared the removed leaf to the joiner. The ordinary validator's rule that would have caught it, validateRemove's not_me check, is deliberately skipped on this path — and correctly so, since a resync commit legitimately removes a leaf the joiner owns — but nothing was substituted for it.

An external party therefore needed only the group's public GroupInfo and a victim's LeafIndex, which is visible in the group's own ratchet tree, to commit a Remove naming any member at all. Every member applied it: the victim was evicted and the attacker's own leaf took the freed slot, with no error or rejected-proposal signal from Group.handle.

A credential comparison did exist, but only in the gRPC interop harness (MLSClientImpl.externalJoinImpl), which is test scaffolding layered over the library. It protected no other caller of the public Group.externalJoin / Group.handle API, and no interoperating implementation whose commits arrive over the wire.

An external commit carrying a Remove is now accepted only where the removed leaf's credential is identical to the one in the joiner's own new leaf, enforced on both the sending and the receiving side. Credentials are compared by their encoding rather than by Credential.getIdentity(), which is populated only for the basic credential type and would have matched any two x509 credentials against each other.

The fix was introduced in commit 7e8bb10eb90b.

Credit: Yu Bao from the PayPal Cyber Security Team.

Clone this wiki locally