
Bitwarden Server v2026.9.2 - Élevé - IDOR - injecter des éléments de coffre déchiffrables dans des collections où l'appelant ne peut pas écrire
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · bitwarden-cipher-create-acl
Bitwarden Server v2026.9.2 - Bitwarden
La création de cipher d'organisation sur le chemin Entity Framework (MariaDB, Postgres, SQLite) abandonne l'id de l'appelant avant l'attachement à la collection. Un membre confirmé peut planter un élément à clé d'organisation dans une collection où il ne peut pas écrire. Les membres de la collection le synchronisent comme un login partagé normal.
| ID | pas encore de CVE |
| CWE | CWE-863 / CWE-639 |
| CVSS | Élevé : 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L |
| Produit | Bitwarden Server |
| Affecté | v2026.9.2 (9ee4e0eb) lite EF (MariaDB / Postgres / SQLite). MSSQL/Dapper conserve @UserId. |
| Auth | membre d'organisation confirmé |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
L'attaquant ajoute des éléments de coffre à une collection partagée. Il ne lit ni ne remplace les secrets déjà présents.
Un membre d'organisation confirmé possède déjà la clé d'organisation, donc le nouvel élément se déchiffre pour tous ceux qui peuvent voir cette collection. Bitwarden l'affiche comme un login, une note, une carte ou une clé SSH partagé normal depuis l'intérieur du coffre de l'entreprise.
Okta, GitHub, VPN, Payroll) dont l'URI pointe vers un hôte qu'ils contrôlent. Les membres de la collection l'ouvrent depuis Bitwarden et atterrissent sur la page de l'attaquant, ou l'auto-remplissent contre un sosie.nouvelle clé AWS, clé SSH bastion, mot de passe DB après changement). Quelqu'un le copie en production ou dans un terminal.Production peut quand même y déposer un nouvel élément sur lite EF.S'ils n'ont aucun accès à la collection cible, POST /api/ciphers/create peut renvoyer 404 après que l'écriture est déjà validée. Les victimes reçoivent quand même l'élément à la synchronisation.
J'ai épinglé bitwarden/server v2026.9.2 (9ee4e0eb) après la vague 2026 : TDE sur demande admin (CVE-2026-60104), troncature SSO ExternalId (CVE-2026-101878), collections vides à l'import d'organisation (CVE-2026-43638). Ceux-là sont fermés. L'ACL de collection à la création ne l'était pas.
CiphersController.PostCreate vérifie seulement l'appartenance à l'organisation, puis passe skipPermissionCheck: true quand OrganizationId est défini. SaveDetailsAsync traite alors « cette collection existe dans l'organisation » comme la seule vérification de collection.
Lite MariaDB/Postgres/SQLite utilisent EF. CreateAsyncReturnCipher définit cipher.UserId = null parce qu'un id d'organisation est présent, puis transmet ce null à UpdateCollectionsAsync. Un id utilisateur null signifie toutes les collections de l'organisation. SQL Server Dapper conserve @UserId pour Cipher_UpdateCollections (ReadOnly = 0). Ce chemin est le négatif.
Fausses pistes déjà consignées : traiter le cloud POST /organizations comme la création self-host (c'est [NotSelfHostedOnly] ; lite veut un fichier de licence, donc le lab initialise les lignes d'organisation après l'enregistrement) ; planter dans une collection que l'appelant ne peut pas lire et lire le HTTP 404 comme un échec (la ligne CollectionCipher est déjà validée) ; supposer que MSSQL lite le montrerait (Dapper n'abandonne pas l'id de l'appelant) ; laisser BW_ENABLE_ADMIN désactivé pour que lite ne migre jamais User / OrganizationDomain (admin est le migrateur EF sur cette image) ; omettre Bitwarden-Client-Version sur /identity/connect/token et voir le password grant renvoyer 400. Un reverse shell. Du théâtre.
cd lab
./run.sh
Cible uniquement http://127.0.0.1:18160. Image ghcr.io/bitwarden/lite:2026.9.2 plus MariaDB.
SUCCESS BITWARDEN-CIPHER-CREATE-ACL create-http=404 collectioncipher=1 victim-sync-has-cipher=yes cipher=<id> negative-http=404 readonly-http=200 BITWARDEN-CIPHER-CREATE-ACL-WITNESS
Le plant sans accès renvoie 404 après que CollectionCipher est validée. GET /api/ciphers et /api/sync de la victime contiennent tous deux le nouvel id. Le lecture seule sur B renvoie 200 et plante quand même. Une collection d'une organisation étrangère reste 404 sans ligne de jointure.
Transmettre l'id de l'utilisateur qui enregistre à EF UpdateCollectionsAsync de la même manière que CipherDetails_CreateWithCollections transmet encore @UserId après avoir mis à null la colonne UserId stockée. Ne pas traiter un utilisateur null comme « toutes les collections » sur le chemin de création par un membre. L'existence dans l'organisation n'est pas un droit d'écriture.
CiphersController.cs (Post, PostCreate)CipherService.cs (SaveDetailsAsync)CipherRepository.cs (CreateAsyncReturnCipher, UpdateCollectionsAsync)