GHSA-2xv8-gjwh-fv8p: CrateDB's Blob HTTP handler bypasses authorization
**Component:** `io.crate.protocols.http.HttpBlobHandler`
**Affected:** verified against CrateDB 6.2.7 (latest at time of report; the bug has existed since the blob HTTP handler was introduced)
**Impact:** any authenticated user can read or delete any blob whose SHA-1 digest they know, and can plant new blobs unconditionally, in any blob table, regardless of `GRANT`s.SummaryCrateDB has two ways to access blob storage: SQL (`SELECT ... FROM blob.<table>` and friends) and the blob HTTP API (`GET|PUT|DELETE /_blobs/{table}/{digest}`). The SQL path goes through `AccessControl`, which is what enforces privilege grants; that's why `SELECT digest FROM blob.secret_blobs` fails for a user who has no grants on the table.The HTTP path authenticates the request but never asks `AccessControl` whether the authenticated user is allowed to touch the table. So a user with no grants gets `MissingPrivilegeException` from SQL and `200 OK` plus the blob bytes from `GET /_blobs/secret_blobs/<digest>`.Where it lives`server/src/main/java/io/crate/protocols/http/HttpBlobHandler.java`. The dispatcher:No `AccessControl` reference, no privilege check. Each branch goes straight to the relevant blob op (`get`/`head`/`put`/`delete`); for example:`grep -n 'AccessControl\|ensureMaySee\|checkPermission' HttpBlobHandler.java` returns nothing.The APIs that should be called here, used by the SQL path before every statement is dispatched:`server/src/main/java/io/crate/auth/AccessControl.java` (interface, declares `ensureMayExecute(...)` and `ensureMaySee(...)`)`server/src/main/java/io/crate/auth/AccessControlImpl.java:133` (concrete impl)Threat modelUnconditional in code, gated in practice by digest knowledge; CrateDB has no enumeration channel. `HEAD /_blobs/<table>/<digest>` is the existence oracle; candidate digests may come from side channels such as app metadata, logs, known-file probes.| Capability | Needs digest? | Impact |
|---|---|---|
| Read or delete a blob | yes | High when digests leak, nil otherwise |
| Plant new blobs (PUT) | no | Storage pollution; SHA-1 check blocks forging under a victim's digest |Digest secrecy is not a documented security boundary.ReproductionEnd-to-end Docker PoC. Two users, one blob, both ingress paths exercised side by side.`./run.sh` brings up a CrateDB container with HBA enabled, creates an `admin` (with `ALL PRIVILEGES`) and an `unprivileged` user (with no grants), uploads a blob as admin, then runs six steps:Admin uploads a blob via `PUT /_blobs/...`. Success (201).Admin reads via SQL. Success.**Unprivileged user reads via SQL.** Denied (correct, this is what we want).**Unprivileged user reads via `GET /_blobs/...`.** `200 OK` plus the blob payload (the bug).**Unprivileged user deletes via `DELETE /_blobs/...`.** `204 No Content` (the bug, again).Admin re-checks via SQL. Confirms the blob is gone, deleted by a user with zero grants.Sample output from a real run:PoC filesHBA rule 0 trusts the built-in `crate` superuser so `setup.sql` can bootstrap users; rule 99 forces password auth for everyone else. `network.host=0.0.0.0` overrides the default `_site_` bind, which fails when Docker's interfaces have no site-local address.FixingPlumb `AccessControl` into `HttpBlobHandler`. Before dispatching the verb at `handleBlobRequest:181`, resolve the connecting role from the channel attribute the auth filter already sets, build an `AccessControlImpl`, and call `ensureHasPrivilege(...)` for the verb. Failures produce `MissingPrivilegeException`, which the existing exception-to-HTTP mapping turns into `403 Forbidden`. SQL and HTTP then share one authorization decision.| HTTP verb | SQL equivalent | Required privilege on `blob.<table>` |
|---|---|---|
| `GET` / `HEAD` | `SELECT` | `DQL` |
| `PUT` | `INSERT` / `UPDATE` | `DML` |
| `DELETE` | `DELETE` | `DML` |Alternatives I'd avoid: pushing checks down into `BlobService` (every caller has to remember to pass a role) or wrapping the handler in a separate Netty filter (works but separates the check from the action it gates).NotesDeployments that don't use `BLOB TABLE` are unaffected. Authentication itself still works; the bug is strictly that being authenticated as anyone is treated as sufficient for any blob op.
CVSS v3.1: 0