8 min čteníJohnny UnarJohnny Unar

Váš replikační účet je epicentrum výbuchu

CVE-2026-6471 promění libovolný Postgres účet s atributem REPLICATION v RCE. Takhle auditujete přihlašovací údaje, na které se nikdo nepodíval od roku 2019.

účet, na který jste zapomněli

Cyera vydala CVE-2026-6471 prvního září a to, co by vám mělo zkazit sobotu, není samotné RCE, ale kde to RCE sedí. Logical decoding v PostgreSQL nemá od verze 9.4, která vyšla v roce 2014, žádnou autorizační kontrolu. To znamená, že každá verze od té doby až po 18 dovolí libovolnému účtu s atributem REPLICATION nasměrovat logical decoding na libovolné soubory viditelné z OS, načíst plugin a dojít rovnou ke spuštění kódu jako OS uživatel postgres. Eskalace na superusera, čtení libovolných souborů a persistentní backdoor, a to všechno z účtu, který většina týmů bere jako druhořadý.

Zamyslete se, kdo v clusteru vlastně REPLICATION drží. Skoro nikdy to není váš app user, protože tam jste byli opatrní, tu roli jste omezili na konkrétní tabulky a odebrali jste všechno, co šlo. Není to ani vaše DBA login, protože ta má MFA na bastionu a pravidelně se rotuje. Replikační přihlašovací údaj je ten, který jste ve spěchu vytvořili před třemi lety, aby Debezium mohlo streamovat CDC do Kafky. Nebo ten, který ve dvě ráno používá váš pg_basebackup cron. Nebo ten, který potřebuje monitorovací agent, aby si přečetl replication lag. Vytvořili jste ho, pipeline se rozsvítila zeleně a už jste se na něj nikdy nepodívali.

A přitom je to často to nejméně chráněné v celém systému. Bývá v plaintextu v configu konektoru, commitnutý v Helm values souboru nebo sedí v environment variable na stroji, kam má SSH patnáct lidí. Nikdo ho neauditovuje, protože se přímo nedotýká zákaznických dat, takže propadne mezerou mezi security týmem, který auditovuje přístup aplikace, a platform týmem, který předpokládá, že si to hlídají DBA. A právě tahle mezera je teď epicentrem výbuchu.

co logical decoding útočníkovi vlastně předá

Abyste pochopili, proč je to tak zlé, musíte pochopit, co vlastně wal_level = logical zapíná. Fyzická replikace jenom streamuje WAL bajty na standby a zatímco REPLICATION účty už v tomhle režimu dokážou napáchat dost škody, logical decoding je jiná zvěř, protože uvnitř backend procesu spouští output plugin, který čte WAL a transformuje ho na logický stream změn. Ten output plugin je sdílená knihovna. Načítá se podle jména. A dokud nepřišel tenhle patch, kód, který ji načítá, nikdy nekontroloval, jestli má volající jakékoliv oprávnění načítat libovolné knihovny.

Útok tedy zhruba vypadá tak, že se účet s REPLICATION připojí, vytvoří logical replication slot a specifikuje output_plugin, který ukazuje na soubor pod kontrolou útočníka nebo na systémovou knihovnu, kterou lze zneužít. Zkombinujte to se schopností donutit server během procesu číst soubory viditelné z OS a máte disclosure čehokoliv, co uživatel postgres přečte, což na typickém stroji zahrnuje SSH klíče, přihlašovací údaje jiných služeb v /etc a obsah samotného data directory. Odtud je eskalace na plné RCE už jen mechanická.

Krutý detail je, že spousta týmů zapnula wal_level = logical před lety kvůli jednomu Debezium konektoru a zapomněli, že je to globální nastavení. Není to per-table, není to per-database, je to na úrovni celého clusteru. Jakmile je zapnuté, každý REPLICATION účet na té instanci se dostane k logical decoding mašinerii, ať už měl s CDC cokoliv společného, nebo ne. Zapnuli jste to kvůli jedné pipeline a potichu rozšířili útočnou plochu celého clusteru.

nejdřív vyjmenujte každý replikační účet

Než se něčeho dotknete, zjistěte, jak zlé to vaše vystavení vlastně je, protože většina týmů opravdu netuší, kolik REPLICATION rolí má. Ten atribut se neukazuje na místech, kam se lidi obvykle dívají. Spusťte tohle:

SELECT rolname, rolsuper, rolreplication, rolbypassrls FROM pg_roles WHERE rolreplication OR rolsuper;

Superuseři mají replikaci implicitně, takže záleží na obou sloupcích. Teď to zkorelujte s pg_hba.conf, protože role s REPLICATION je zneužitelná jen tehdy, pokud se s ní něco skutečně dokáže autentizovat přes replikační nebo běžné připojení. Vygrepněte si v hba souboru přesné klíčové slovo:

grep -nE 'replication' $(psql -tA -c 'SHOW hba_file')

A zkontrolujte pg_stat_replication a pg_replication_slots, abyste viděli, které z těchto účtů se právě teď opravdu používají a které jsou opuštěné:

SELECT slot_name, plugin, slot_type, active, active_pid FROM pg_replication_slots;

Minulý měsíc jsme tohle cvičení dělali na klientově clusteru a našli jsme čtyři REPLICATION role. Jedna byla Debezium, jedna byl standby vyřazený z provozu v roce 2023, jehož přihlašovací údaj byl ale pořád platný, jedna byl monitorovací agent, který potřeboval jen pg_stat přístup a REPLICATION neměl mít nikdy, a jednu nikdo nedokázal identifikovat. A přesně o tu poslední tady jde. Pokud nedokážete pojmenovat člověka nebo systém, který REPLICATION údaj vlastní, a vysvětlit, proč ten atribut potřebuje, hned ho zahoďte přes ALTER ROLE foo NOREPLICATION a uvidíte, co se rozbije. Skoro nic, protože monitorovací agenti čtoucí replication lag přes pg_stat view ten atribut nepotřebují, potřebují ho jen skuteční konzumenti replikace.

co patch vlastně změnil

Oprava není jen autorizační kontrola přišroubovaná na starý kód. Zavádí nový GUC jménem output_plugin_libraries, který funguje jako allowlist knihoven, které smí logical decoding načítat. Po patchi je defaultně povolená množina prázdná až na pluginy, které se dodávají přímo s Postgresem, což znamená, že útočník specifikující libovolné jméno knihovny dostane rejection dřív, než se cokoliv načte. Pokud jedete pgoutput pro nativní logickou replikaci nebo vendored plugin jako wal2json, musíte je explicitně vyjmenovat, a to je správný tradeoff, protože vás nutí deklarovat přesně, kterých decoding knihoven se váš cluster smí dotknout.

Takže až zapatchujete na opravený minor release pro svou major verzi, nepředpokládejte, že vám CDC pipeline pojede dál. Pokud používáte wal2json nebo decoderbufs nebo cokoliv jiného než pgoutput, budete potřebovat něco jako:

output_plugin_libraries = 'pgoutput,wal2json'

v postgresql.conf a reload. Otestujte to ve stagingu, než patchnete produkci, protože failure mode je ten, že váš Debezium konektor najednou selhává při vytváření slotů s chybou, že plugin není povolený. A pokud tohle zjistíte během incidentu ve dvě ráno, radost mít nebudete.

Ten allowlist je opravdu dobrý design, protože mění implicitní hranici důvěry na explicitní. Předtím server načetl cokoliv, co jste pojmenovali. Teď načte jen to, co jste deklarovali, což je způsob, jakým to mělo fungovat už v roce 2014.

zpevněte pg_hba.conf, aby nezapatchovaný stav nebyl fatální

Skutečná oprava je patch, ale pokud jedete managed Postgres, kde vendor opravený minor ještě nevydal, nebo máte flotilu, kde patchování trvá týden, můžete výrazně snížit vystavení na úrovni autentizace, protože útok vyžaduje funkční replikační nebo superuser připojení a o tom, kdo ho dostane, rozhoduje pg_hba.conf.

Začněte tím, že replikační řádky uděláte tak úzké, jak to fyzicky jde. Depresivně častý vzor vypadá takhle:

host replication all 0.0.0.0/0 md5

což dovolí libovolné REPLICATION roli autentizovat se odkudkoliv. Zabijte to. Replikace by měla být zamčená na přesnou CIDR vašich standbyů nebo CDC hostu, měla by používat scram-sha-256, nikdy md5, a ideálně ověřování klientského certifikátu:

hostssl replication debezium 10.20.4.11/32 cert clientcert=verify-full

Epicentrum výbuchu se hodně zmenší, když je přihlašovací údaj k ničemu, dokud nepřichází z jednoho konkrétního hostu předkládajícího jeden konkrétní klientský cert. I když config konektoru unikne, útočník navíc potřebuje síťovou pozici a platný cert, aby s ním cokoliv udělal.

Jedna jemnost, kterou lidi přehlížejí: logical decoding se dá řídit přes běžné databázové připojení, ne jen přes dedikované replikační. Takže zpřísnit jen 'replication' řádky v hba nestačí, pokud se ta samá role dokáže připojit jako běžný klient a zavolat pg_create_logical_replication_slot. Omezte i běžnou konektivitu té role, a pokud se CDC účet připojuje vždy jen z jednoho hostu, přišpendlete jeho normální databázové řádky na tu samou /32. Zkombinujte úzká hba pravidla s NOREPLICATION na každé roli, která to striktně nepotřebuje, a z triviálního exploitu se stane něco, co vyžaduje, aby útočník už seděl na vašem standby subnetu s ukradeným certem, což je s incident týmem úplně jiná konverzace.

skutečné ponaučení

Tahle CVE je dobrá záminka opravit celou kategorii problému, ne jen jednu chybu. Strojové přihlašovací údaje, které se jednou vytvoří během projektu a pak žijí navěky, jsou místem, kde spousta reálných breachů skutečně začíná, a mají jednu ošklivou vlastnost: jsou moc nudné na to, aby je auditovala security, a moc infrastrukturní na to, aby si je vzal do vlastnictví app tým. Takže hnijí v mezeře mezi nimi.

Když ve steezru děláme klientům security práci, replikační a servisní účty jsou skoro vždycky ta část review, která lidi překvapí, protože každý si vzpomene omezit app roli a nikdo si nevzpomene na údaj ke konektoru z migrace, kterou dělali před dvěma lety. Zabudujte ten audit do něčeho opakovaného. Čtvrtletní job, který vypíše každou rolreplication a rolsuper roli, zkorreluje ji s aktivními sloty a pingne člověka, aby každou zdůvodnil, napíšete za odpoledne a chytí opuštěný standby údaj dřív, než se stane vstupním bodem CVE-2026-6471.

Zapatchujte na opravený minor, nastavte output_plugin_libraries, zahoďte REPLICATION ze všeho, co replikaci nekonzumuje, a přišpendlete hba pravidla na konkrétní hosty se scram a certy. Pak si zapište, kdo vlastní každý údaj, který přežije, protože další logical-decoding bug přijde a dostanou vás právě ty účty, na které si nikdo nevzpomene.

Johnny Unar

Napsal/a

Johnny Unar

Chcete s námi spolupracovat?

CVE-2026-6471 promění libovolný Postgres účet s atributem REPLICATION v RCE. Takhle auditujete přihlašovací údaje, na které se nikdo nepodíval od roku 2019.