GHSA-6j7p-qjhg-9947: Rucio has SQL Injection in FilterEngine PostgreSQL Query Builder via DID Search API
SummaryA SQL injection vulnerability in `FilterEngine.create_postgres_query` allows any authenticated Rucio user to execute arbitrary SQL against the configured PostgreSQL metadata database through the DID search endpoint (`GET /dids/<scope>/dids/search`). When the external metadata plugin `postgres_meta` is configured, attacker-controlled filter keys and values are interpolated directly into raw SQL statements via Python `str.format`. This enables full database compromise including data exfiltration, data modification, and potential remote code execution via `COPY ... FROM PROGRAM`.DetailsThe vulnerability exists in `lib/rucio/core/did_meta_plugins/filter_engine.py` within the `create_postgres_query()` method (lines 408-484). This method builds raw SQL strings via Python `.format()` across 6 distinct injection points:**filter_engine.py:477** (string equality — default branch):**filter_engine.py:442** (wildcard/LIKE branch):**filter_engine.py:456** (boolean branch — value unquoted):**filter_engine.py:462** (numeric branch — value unquoted):**filter_engine.py:472** (datetime branch):**filter_engine.py:479** (non-JSONB column fallback):Both `key` and `value` are attacker-controlled strings derived from HTTP query parameters. The resulting `expression` string is concatenated into a larger query string (`postgres_query_str`) that is then passed to `psycopg3`'s `sql.SQL()`:`sql.SQL()` wraps the string as a trusted SQL syntax fragment — it does **not** escape or parameterize its contents. The statement is then executed via `cur.execute(statement)` at `postgres_meta.py:321`.Why no existing defense blocks thisThe data flow from HTTP request to SQL execution passes through multiple layers with no effective sanitization:**HTTP input** (`dids.py:265-274`): Filter keys and values are accepted from query parameters via `ast.literal_eval()` or directly from individual query argument names/values. The fallback path only excludes 4 reserved keys (`type`, `limit`, `long`, `recursive`).**Plugin routing** (`did_meta_plugins/__init__.py:227-248`): Each filter key is checked via `manages_key()`. `postgres_meta.manages_key()` **unconditionally returns `True`** (line 345) — it accepts ANY filter key without validation.**FilterEngine initialization**: The `postgres_meta` plugin instantiates `FilterEngine` with `strict_coerce=False`. Unknown keys pass through `_coerce_filter_word_to_model_attribute()` as raw strings.**Value typecasting** (`filter_engine.py:275-297`): `_try_typecast_string()` attempts to parse the value as a boolean, datetime, or number. SQL injection strings fail all these parsers and are returned unchanged.**Sanity checks** (`filter_engine.py:149-190`): `_sanity_check_translated_filters()` does **not** validate arbitrary key names or values for SQL-unsafe characters.**SQL construction** (`filter_engine.py:442-479`): The unsanitized key and value strings are interpolated directly into raw SQL strings via `.format()`.**SQL execution** (`postgres_meta.py:316,321`): The raw string is wrapped in `sql.SQL()` (treated as trusted SQL) and executed via `cur.execute()`.PoC**Prerequisites:**A Rucio instance using PostgreSQL as the database backendThe `postgres_meta` metadata plugin **explicitly configured** (this is NOT the default — the default is `json_meta`)Any valid Rucio authentication token (obtainable via userpass, x509, OIDC, SAML, SSH, or GSS)1. Obtain an authentication token2. Value injection — boolean-based filter bypass3. Key injection via query parameter name4. UNION-based data extraction5. Stacked queries — data modification6. Remote code execution (if database user has superuser privileges)7. Alternative entry via `filters` query parameterImpact**Vulnerability type:** SQL Injection (CWE-89)**CVSS v3.1:** 9.9 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)**Who is impacted:**Rucio deployments that have explicitly configured the `postgres_meta` metadata plugin.**What an attacker can do:****Data modification:** PostgreSQL stacked queries enable arbitrary `INSERT`/`UPDATE`/`DELETE` operations.**Remote code execution:** Via PostgreSQL's `COPY ... FROM PROGRAM` if the database user has superuser or `pg_execute_server_program` privileges.**File system access:** Via `COPY ... TO/FROM '/path'` if filesystem permissions allow.**Further elevation when the same postgres database and access is used for metadata and for Rucio itself****Full database read access:** Extract any table including `identities` (password hashes and salts), `tokens` (active authentication sessions), `accounts` (user enumeration), `rse_settings` (storage endpoint credentials), and `rules` (data management policies) could be extracted.**Password hash extraction:** Combined with Rucio's use of single-iteration SHA-256 for password hashing (no KDF), extracted hashes can be cracked at GPU speed.**Authentication token theft:** Active bearer tokens can be extracted and used for immediate session hijacking.**Required attacker privileges:** Any authenticated Rucio user. Authentication tokens can be obtained via any supported method (userpass, x509, OIDC, SAML, SSH, GSS). No special roles or administrative permissions are required. The `GET /dids/<scope>/dids/search` endpoint is available to all authenticated users.
CVSS v4.0: 9
CVSS v3.1: 9.9