FlawOpen

flawopen.com/Incidents/postgresql-psql-injection-beyondtrust-treasury-december-2024

● PostgreSQL / BeyondTrust · CVE-2025-1094 · December 2024 – February 2025 · CVSS 8.1 · Hoch
Sicherheitsforschung · FlawOpen

CVE-2025-1094: SQL-Injection in PostgreSQL-psql

Ungültiges UTF-8 umging libpq-Escaping, wenn Apps damit psql-Eingaben erstellten; SQL oder \! konnte injiziert werden. Behoben am 13. Feb. 2025.

💡 Einfache Erklärung (ELI5)

Stellen Sie sich einen Übersetzer vor, dessen Aufgabe es ist, alles, was Sie sagen, in Anführungszeichen zu setzen, damit es nie für einen Befehl gehalten wird. Sie merken: Geben Sie ihm ein Wort in einem Alphabet, das er nicht kennt, gerät er durcheinander und lässt ein Anführungszeichen weg. Ihr Satz entkommt den Anführungszeichen und wird als Anweisung gelesen – obwohl der Übersetzer genau das tat, wofür er gebaut war, bei allen Eingaben, die je jemand getestet hatte.

Kernkonzepte & Begriffe

PQescapeLiteral
Eine libpq-Funktion, die einen Wert in Anführungszeichen setzt und maskiert, damit er in SQL-Text eingefügt werden kann. Auch PQescapeIdentifier, PQescapeString und PQescapeStringConn waren betroffen.
Ungültiges UTF-8
Eine Bytefolge, die in der angegebenen Kodierung nicht wohlgeformt ist. Escape-Code und psql waren sich uneinig, wie viele folgende Zeichen solche Bytes verschlucken.
psql-Metabefehl
Ein Befehl mit Backslash, den der psql-Client statt des Servers verarbeitet. \! führt den Rest der Zeile als Shell-Befehl des Betriebssystems aus.
CVE-2024-12356
Eine kritische Command-Injection-Lücke in BeyondTrust Privileged Remote Access und Remote Support. In Rapid7s Tests erforderte Codeausführung über sie CVE-2025-1094.
Gebundener Parameter
Ein Wert, der getrennt vom SQL-Text an den Server geht, sodass über ihn nie eine Entscheidung zu Anführungszeichen oder Maskierung getroffen wird.

Framework-Zuordnungen

  • CWE-Referenz für die in diesem Leitfaden beschriebene Schwachstelle: CWE-89 CWE-149

Vorfall-Zeitstrahl

2024-12-16

BeyondTrust veröffentlicht das Advisory BT24-10 zu CVE-2024-12356, nachdem es die Kompromittierung einiger Remote-Support-SaaS-Instanzen untersucht hat; das US-Finanzministerium meldete später, dass dieser Vorfall seine Systeme erreichte.

2024-12-19

Die CISA nimmt CVE-2024-12356 in ihren Katalog Known Exploited Vulnerabilities auf.

2025-02-13

PostgreSQL veröffentlicht 17.3, 16.7, 15.11, 14.16 und 13.19 mit der Korrektur für CVE-2025-1094, und Rapid7 veröffentlicht seine Analyse.

Schritt-für-Schritt Angriffsablauf

Schritt 1

Eingabe erreicht eine Escape-Funktion

Eine Anwendung übernimmt vom Angreifer beeinflussten Text und gibt ihn an PQescapeLiteral oder eine verwandte libpq-Funktion, um eine SQL-Anweisung zu bauen.

Schritt 2

Ungültiges UTF-8 versteckt ein Anführungszeichen

Laut PostgreSQL-Advisory führte eine präparierte ungültige Mehrbytefolge vor einem einfachen Anführungszeichen dazu, dass die Escape-Routine dieses Zeichen nicht maskierte.

Schritt 3

psql beendet das Literal zu früh

Wird die Anweisung an psql übergeben, schließt das unmaskierte Anführungszeichen das String-Literal, und der Rest der Eingabe wird als SQL oder als psql-Befehl gelesen.

Schritt 4

Ein Metabefehl startet eine Shell

Rapid7 zeigte, dass der eingeschleuste Text den Metabefehl \! enthalten kann, den psql als Betriebssystembefehl auf dem Rechner ausführt, auf dem es läuft.

Quellcode: Verwundbar vs. Sicher

Illustratives Python mit psycopg 3, nicht BeyondTrusts Code. Die verwundbare Version bildet das riskante Muster aus dem Advisory nach: einen Wert mit libpq maskieren, in SQL-Text einfügen und diesen Text an psql weiterreichen. Die gehärtete Version beseitigt beide Hälften: Sie weist fehlerhaftes UTF-8 vor jeder Verarbeitung ab und sendet den Wert als gebundenen Parameter über einen echten Treiber, sodass es keinen Escape-Schritt zum Austricksen und keinen psql-Metabefehlsparser im Pfad gibt. Ein libpq-Update bleibt für jeden Code wichtig, der weiterhin maskiert.

VERWUNDBARE IMPLEMENTIERUNG
import subprocess

import psycopg


def lookup_user(conn: psycopg.Connection, name: bytes) -> bytes:
    # PQescapeLiteral aus libpq (vor 17.3/16.7/15.11/14.16/13.19) konnte
    # ein Anführungszeichen neutralisieren, das hinter einer ungültigen UTF-8-Bytefolge verborgen war.
    literal = conn.pgconn.escape_literal(name)
    script = b"SELECT id FROM users WHERE name = " + literal + b";\n"
    # psql führt seine Eingabe als Skript aus, einschließlich Metabefehlen wie \!
    result = subprocess.run(["psql", "-At", "-d", "app"], input=script,
                            capture_output=True, check=True)
    return result.stdout
GEHÄRTETER SICHERHEITS-PATCH
import psycopg

MAX_NAME_CHARS = 128


def lookup_user(conn: psycopg.Connection, name: bytes) -> list[int]:
    try:
        text = name.decode("utf-8")  # reject malformed input at the boundary
    except UnicodeDecodeError:
        raise ValueError("name is not valid UTF-8") from None
    if len(text) > MAX_NAME_CHARS:
        raise ValueError("name too long")
    with conn.cursor() as cur:
        # Der Wert wird als gebundener Parameter getrennt vom SQL-Text übergeben,
        # und es ist kein psql-Prozess (und damit kein Metabefehls-Parser) beteiligt.
        cur.execute("SELECT id FROM users WHERE name = %s", (text,))
        return [row[0] for row in cur.fetchall()]

Checkliste für Engineering & Systemsicherheit

Quellen