
Bitwarden Server v2026.9.2 - Alta - IDOR - inserire elementi di cassaforte decifrabili in collezioni in cui il chiamante non può scrivere
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · bitwarden-cipher-create-acl
Write-up: https://abraxaslabs.tech/research/bitwarden-cipher-create-acl
Bitwarden Server v2026.9.2 - Bitwarden
La creazione di un cipher di organizzazione sul percorso Entity Framework (MariaDB, Postgres, SQLite) scarta l'id del chiamante prima del collegamento alla collection. Un membro confermato può inserire un elemento con chiave di organizzazione in una collection in cui non può scrivere. I membri della collection lo sincronizzano come un normale login condiviso.
| ID | nessuna CVE al momento |
| CWE | CWE-863 / CWE-639 |
| CVSS | Alto: 7.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L |
| Prodotto | Bitwarden Server |
| Affetto | v2026.9.2 (9ee4e0eb) lite EF (MariaDB / Postgres / SQLite). MSSQL/Dapper mantiene @UserId. |
| Autenticazione | membro confermato dell'organizzazione |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
L'attaccante aggiunge elementi alla cassaforte in una collection condivisa. Non legge né sostituisce i segreti già presenti.
Un membro confermato dell'organizzazione possiede già la chiave dell'organizzazione, quindi il nuovo elemento viene decifrato da chiunque possa vedere quella collection. Bitwarden lo mostra come un normale login, nota, carta o chiave SSH condivisa dall'interno della cassaforte aziendale.
Okta, GitHub, VPN, Payroll) il cui URI punta a un host controllato dall'attaccante. I membri della collection lo aprono da Bitwarden e finiscono sulla pagina dell'attaccante, oppure lo usano per l'auto-fill su un sito clone.new AWS key, bastion SSH key, DB password after change). Qualcuno lo copia in produzione o in un terminale.Production può comunque inserirvi un nuovo elemento su lite EF.Se non hanno accesso alla collection di destinazione, POST /api/ciphers/create può restituire 404 dopo che la scrittura è già stata confermata. Le vittime ricevono comunque l'elemento alla sincronizzazione.
Ho fissato bitwarden/server v2026.9.2 (9ee4e0eb) dopo l'ondata del 2026: TDE su richiesta admin (CVE-2026-60104), troncamento di SSO ExternalId (CVE-2026-101878), import di organizzazione con collection vuote (CVE-2026-43638). Queste sono chiuse. L'ACL della collection in fase di creazione non lo era.
CiphersController.PostCreate verifica solo l'appartenenza all'organizzazione, poi passa skipPermissionCheck: true quando OrganizationId è impostato. SaveDetailsAsync considera poi "questa collection esiste nell'organizzazione" come unico controllo sulla collection.
Lite MariaDB/Postgres/SQLite usano EF. CreateAsyncReturnCipher imposta cipher.UserId = null perché è presente un id di organizzazione, poi passa quel null a UpdateCollectionsAsync. Un user id null significa ogni collection dell'organizzazione. SQL Server Dapper mantiene @UserId per Cipher_UpdateCollections (ReadOnly = 0). Quel percorso è il negativo.
Errori già registrati: trattare il cloud POST /organizations come la creazione self-host (è [NotSelfHostedOnly]; lite richiede un file di licenza, quindi il lab inserisce le righe org dopo la registrazione); inserire in una collection che il chiamante non può leggere e interpretare l'HTTP 404 come un miss (la riga CollectionCipher è già committata); presumere che MSSQL lite lo mostrerebbe (Dapper non elimina l'id del chiamante); lasciare BW_ENABLE_ADMIN disattivato così che lite non migri mai User / OrganizationDomain (admin è il migratore EF su questa immagine); omettere Bitwarden-Client-Version su /identity/connect/token e osservare il password grant restituire 400. Una reverse shell. Teatro.
cd lab
./run.sh
Prendi di mira solo http://127.0.0.1:18160. Immagine ghcr.io/bitwarden/lite:2026.9.2 più 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
La pianta senza accesso restituisce 404 dopo il commit di CollectionCipher. La vittima GET /api/ciphers e /api/sync contengono entrambi il nuovo id. La lettura su B restituisce 200 e inserisce comunque. La collection di un'organizzazione esterna resta 404 senza riga di join.
Passare l'id dell'utente che salva in UpdateCollectionsAsync di EF allo stesso modo in cui CipherDetails_CreateWithCollections passa ancora @UserId dopo aver impostato a null la colonna UserId memorizzata. Non trattare un utente null come "tutte le collection" nel percorso di creazione del membro. L'esistenza nell'organizzazione non equivale al permesso di scrittura.
CiphersController.cs (Post, PostCreate)CipherService.cs (SaveDetailsAsync)CipherRepository.cs (CreateAsyncReturnCipher, UpdateCollectionsAsync)