VAITP Dataset

← Back to the dataset

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.

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 query

Patched 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 ::