CVE-2026-29090
Authenticated SQL injection in Rucio's DID search allows arbitrary SQL.
- CVSS 9.0
- CWE-89
- Input Validation and Sanitization
- Remote
### Summary A SQL injection vulnerability exists in Rucio versions 1.30.0 and later before 35.8.5, 38.5.5, 39.4.2, and 40.1.1, in `FilterEngine.create_postgres_query()`. This allows any authenticated Rucio user to execute arbitrary SQL against the PostgreSQL metadata database through the DID search endpoint (`GET /dids/<scope>/dids/search`). When the `postgres_meta` metadata plugin is configured, attacker-controlled filter keys and values are interpolated directly into raw SQL strings via Python `.format()`, then passed to `psycopg3`'s `sql.SQL()` which treats the string as trusted SQL syntax. Depending on the database privileges assigned to the service account, exploitation can expose sensitive tables, modify or delete metadata, access server-side files, or achieve code execution through PostgreSQL features such as COPY … FROM PROGRAM. This issue affects deployments that explicitly use the postgres_meta metadata plugin. This vulnerability has been fixed in versions 35.8.5, 38.5.5, 39.4.2, and 40.1.1.
- CWE
- CWE-89
- CVSS base score
- 9.0
- Published
- 2026-05-06
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- SQL Injection
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- Rucio
- Fixed by upgrading
- Yes
Solution
Upgrade to Rucio versions 35.8.5, 38.5.5, 39.4.2, or 40.1.1.
Vulnerable code sample
import psycopg
from psycopg import sql
# This code is a simplified representation of the vulnerable part of Rucio's
# FilterEngine, based on the description of CVE-2026-29090.
# The actual production code is more complex.
class FilterEngine:
def create_postgres_query(self, filters: dict[str, str]):
"""
Creates a SQL query for PostgreSQL from a dictionary of filters.
This method is vulnerable to SQL injection because it uses Python's
.format() method to insert user-controlled filter keys and values
directly into the SQL query string.
"""
base_query = "SELECT scope, name FROM dids WHERE "
where_clauses = []
for key, value in filters.items():
# VULNERABILITY: User-controlled 'key' and 'value' are formatted
# directly into the string. An attacker can provide a malicious
# 'value' like "'whatever' OR 1=1; --" to manipulate the query.
clause = "meta->>'{}' = '{}'".format(key, value)
where_clauses.append(clause)
if not where_clauses:
return None
# The clauses are joined and appended to the base query
full_query_string = base_query + " AND ".join(where_clauses)
# The resulting string, which may contain injected SQL, is then
# passed to sql.SQL(), which treats the entire string as trusted.
# This is the final step that allows the injection to be executed.
query = sql.SQL(full_query_string)
return queryPatched code sample
from psycopg import sql
def create_postgres_query(filters: dict) -> sql.Composed:
"""
Safely creates a PostgreSQL query from a dictionary of filters,
representing the fix for the described SQL injection vulnerability.
This version uses psycopg's composition API to prevent SQL injection.
Filter keys (column names) are treated as identifiers and filter values
are treated as literals, ensuring they are properly quoted and escaped
by the database driver, rather than being formatted directly into the
SQL string.
"""
if not filters:
# Handle the case where no filters are provided
return sql.SQL("SELECT scope, name, type, guid FROM dids_meta")
query_parts = [sql.SQL("SELECT scope, name, type, guid FROM dids_meta WHERE ")]
where_clauses = []
for key, value in filters.items():
# The fix: Use sql.Identifier for the column name (key) and
# sql.Literal for the value. This prevents user input from being
# interpreted as SQL syntax.
clause = sql.SQL("{key} = {value}").format(
key=sql.Identifier(key),
value=sql.Literal(value)
)
where_clauses.append(clause)
# Join all filter clauses with "AND"
query_parts.append(sql.SQL(" AND ").join(where_clauses))
# Combine all parts into a final, safe query object
return sql.Composed(query_parts)Payload
name=x'); SELECT pg_sleep(10); --
Cite this entry
@misc{vaitp:cve202629090,
title = {{Authenticated SQL injection in Rucio's DID search allows arbitrary SQL.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-29090},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-29090/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
