tom@home.htb:~$

Blog o HTB

29 December 2020

Message brokery a interní fronty jako zdroj tajemství

Úvod a kontext

Message broker nebo interní fronta se v architektuře často bere jako technický detail. Aplikace něco pošle do Redis queue, MQTT topicu nebo jiného interního kanálu a někdo jiný to zpracuje. Jenže právě tím se z brokeru stává bezpečnostní hranice. Nenese jen data. Nese provozní instrukce, stavy workflow, někdy i privátní klíče, tokeny nebo celé job payloady.

PlayerTwo, Ready a Postman ukazují tři různé podoby stejného problému:

Tento text záměrně nenavazuje na storage článek přes “špatně vystavená data”. Tady je důležité něco jiného: co všechno se v interních zprávách a frontách přenáší a proč se z transportní vrstvy tak snadno stane zdroj tajemství nebo rovnou řídicí rozhraní.

Proč jsou message brokery a fronty bezpečnostně tak citlivé

Nesou víc než business data

Ve zprávách často necestuje jen uživatelský obsah. Cestuje v nich i:

Bývají považované za “interní”

Redis, MQTT a podobné systémy často běží:

Jakmile se ale útočník dostane dovnitř hostu, do kontejneru nebo k SSRF, mění se broker z interního pomocníka v plnohodnotnou attack surface.

Jsou to řídicí kanály, ne jen úložiště

Rozdíl proti prostému storage je zásadní. Když aplikace čte soubor, je to statický artefakt. Když čte queue nebo topic, očekává:

Právě to dává message brokerům tak vysokou hodnotu.

Jak tenhle vzorec vypadal v praxi

PlayerTwo: MQTT topic s privátním klíčem

PlayerTwo je nejčistší ukázka toho, že interní broker umí nést rovnou tajemství. Po prvním footholdu jako www-data bylo možné připojit se na lokální Mosquitto broker:

mosquitto_sub -h localhost -t '$SYS/#'

Na tématu $SYS/internal/firmware/signing se periodicky objevoval privátní klíč používaný k podepisování firmwaru. Ten byl zároveň použitelný jako SSH klíč pro účet observer.

To je důležitá lekce. Problém neležel v MQTT jako protokolu. Problém ležel v tom, že provozní tajemství cestovalo v topicu, který byl považovaný za interní a bezpečný jen proto, že broker běžel lokálně.

Ready: Redis queue jako cesta ke spuštění jobu

Na Ready šel GitLab zneužít přes SSRF a CRLF injection tak, aby poslal vlastní příkazy do lokálního Redis. Praktický dopad nebyl “přečtu si klíč z Redis”. Dopad byl vyšší: Redis sloužil jako queue backend pro GitLab joby a útočník tak mohl vložit vlastní job pro GitlabShellWorker.

To je dobré rozlišovat. Redis zde nebyl zajímavý jako key-value store s tajemstvím. Byl zajímavý jako řídicí kanál, přes který aplikace spouštěla vlastní pracovní úlohy. Jakmile se do něj dalo zapisovat, nešlo už o leak, ale o přímý vliv na chování backendu.

Postman: Redis jako hranice mezi storage, queue a správou

Postman je užitečný kontrastní případ. Otevřený Redis zde primárně nevedl přes interní fronty, ale přes CONFIG SET, dbfilename a SAVE k zápisu authorized_keys. Na první pohled to vypadá spíš jako storage chyba než message-broker problém.

Právě to je ale didakticky cenné. V reálném prostředí jeden Redis často plní více rolí najednou:

Postman proto připomíná důležitou otázku: když najdete Redis, nemá smysl řešit jen “co v něm je teď”. Smysl má řešit i “co všechno přes něj aplikace a okolní systém dělají”.

Co je na těchto případech společné

1. Broker nese implicitní důvěru

Aplikace a pracovníci kolem ní předpokládají, že:

Jakmile tento předpoklad padne, dopad bývá větší než u obyčejného datového leaku.

2. Tajemství se v provozu šíří dál, než by měla

PlayerTwo ukazuje privátní klíč v MQTT. Ready ukazuje job payload vedoucí ke spuštění příkazu. V obou případech nejde o “tajemství uložené v DB”. Jde o tajemství nebo instrukci šířenou provozním kanálem, který nikdo neauditoval jako citlivou vrstvu.

3. Interní port neznamená malý dopad

Tyto systémy často neběží veřejně. Přesto mají po footholdu velmi vysokou hodnotu, protože sedí přesně mezi aplikací a její orchestrací. Odtud už bývá blízko:

Jak podobné problémy hledat po footholdu

Po prvním shellu nebo po úspěšné SSRF dává smysl cíleně zjišťovat:

U lokálně běžících služeb typicky pomůže:

ss -lntp
ps aux

A pak už záleží na konkrétním systému:

Jak si to nesplést se storage a localhost články

Tohle téma se přirozeně dotýká i jiných oblastí, ale je užitečné ho držet samostatně.

Jednoduchá otázka pomáhá dobře: je hlavní hodnota v tom, že služba něco ukládá, nebo v tom, že přes ni někdo někomu říká, co má udělat? Pokud platí druhé, je to spíš message broker než storage problém.

Obrana a bezpečnější návrh

1. Neposílat tajemství v provozních zprávách

Privátní klíče, dlouhodobé tokeny a jiné citlivé artefakty nemají cestovat přes MQTT topic, Redis queue nebo podobný interní kanál. Jakmile takový kanál někdo zpřístupní nebo odposlechne, dopad je okamžitý.

2. Oddělit producenty a konzumenty pomocí autentizace a ACL

Broker nemá důvěřovat každému klientovi jen proto, že je “lokální” nebo “ze správné aplikace”. Témata a fronty potřebují:

3. Hlídat, co přes queue skutečně spouští backend

Ready dobře ukazuje, že queue není neutrální vrstva. Pokud worker interpretuje zprávu jako job s vysokou důvěrou, je potřeba přísně kontrolovat, kdo a jak takovou zprávu může vytvořit.

4. Nenechávat Redis a podobné systémy v “interním defaultu”

Postman připomíná, že vystavený Redis je problém i tehdy, když aplikace nikde nepíše slovo “message queue”. Jeden produkt může být současně cache, fronta i admin backend. Bez autentizace nebo segmentace je jeho dopad příliš široký.

5. Monitorovat nečekané subscription a zápisy

Na produkci je podezřelé:

Shrnutí klíčových poznatků

Co si odnést do praxe

tags: message-broker - mqtt - redis - queues - secrets