V 90. letech jsem trochu přičuchnul k programování podobných kritických aplikací a vždy platilo, že pokud v programu používám citlivá data (heslo, klíč, ale i rozšifrovanou komunikaci), musím je držet v paměti jen po nezbytnou dobu a před uvolněním pamětí vymazat (přepsat nejlépe náhodným řetězcem). Právě proto, aby pak někdo nedostal alokovanou paměť se zbytkem citlivých údajů. Toto je absolutní požadavek a má přednost před jakýmikoliv nevýhodami (snížení propustnosti, ...).
Jak je možné, že tak základní pravidlo ignorují vývojáři OpenSSL? To to opravdu psala tlupa nadšenců bez základních znalostí o bezpečném programování?
Rozšifrovanou komunikaci musí OpenSSL předat dál, třeba webovému serveru. A nemůže ovlivnit, co s tím bude aplikace dál dělat, jestli to webový server bude držet v paměti jen po nezbytnou dobu a před uvolněním paměti data přepíše. Webový server by musel vymazávat každý kousek paměti, který uvolňuje. To je nereálné.
Dnes se za správné řešení (pokud už chcete nebo musíte používat jazyk s explicitním memory managementem) považuje používat takové implementace malloc a free které znemožňují podobné úniky dat. Chtít po programátorech aby data z paměti mazali přímo v kódu své aplikace je pro ně spousta práce a hlavně se v tom velmi lehce dá udělat chyba.
Bohužel vývojáři OpenSSL si udělali vlastní implementaci malloc a free která je z pohledu bezpečnosti úplně špatně, a tím vedla k tomuto problému (Heartbleed).
Víc se dá dočíst například na http://it.slashdot.org/story/14/04/10/1343236/theo-de-raadts-small-rant-on-openssl
Víc se dá dočíst například na http://it.slashdot.org/story/14/04/10/1343236/theo-de-raadts-small-rant-on-openssl
Panebože, zneuznaný sociopat opět radí. Kdy zas bude nějaká "pomooooc, krachujeme" sbírka na jeho OS?
Jenomže problém je, když dostanu třeba čtyřkilový blok vedle používanýho 16kilovýho, kde jsou citlivý data.
Takže nestačí jenom mazat RAMku, ale ještě je celkem dobrý validovat komunikaci s vnějším světem. "A podmínka if(RealDataLength != ReceivedDataLength) throw EBrokenPacketError" nesmí nikde chybět. Průšvih je, že tohle je tak triviální věc, že každý bere jako samozřejmost existenci téhle podmínky a ani se neobtěžovali napsat na to nějaký unit test!
Mýlíte se, neposílají se data uvolněná z heapu. Princip chyby je v tom, že se vezme délka payloadu podstrčená útočníkem, alokuje se potřebný buffer pro odpověď a do toho bufferu se zkopíruje payload dat z requestu. Co je v paměti kolem requestu nikdo neví, může to být alokovaná i nealokovaná pamět. Více viz
http://blog.cryptographyengineering.com/2014/04/attack-of-week-openssl-heartbleed.html
"alokuje se potřebný buffer pro odpověď a do toho bufferu se zkopíruje payload dat z requestu"
Nemělo by tohle náhodně způsobovat segfault? Když bude buffer v paměti umístěn kousek od konce alokované paměti pro daný proces, tak si při čtení sáhne mimo tento alokovaný prostor a celkem nutně bude docházet k segfaultům ne?
Nebo je nějakou jinou "vlastností" openssl zaručeno, že ten buffer bude vždy dostatečně daleko od hranice paměti? Jestli mají vlastní malloc, tak je možné asi všechno.
Ano, může to způsobit segfault, ale ve většině případů se to nestane. Pokud má OpenSSL vlastní alokátor, tak ten si naalokuje systémovým alokátorem velký kus paměti, ve kterém si potom dělá svoje alokace. Pravděpodobnost, že by data requestu byly na konci bloku, za kterým už není 64 KB paměti, je malá.
Nene. Já jsem to pochopil takhle:
1. Útočník pošle ping s daty 16 Bytů a požadavkem na 64kB.
2. Server alokuje paměť pro přijatý paket (řekněme 4kB ve widlích - minimální objem). Co je uloženo kolem je mu jedno.
3. Server odešle požadovaných 64kB, ty 4kB jsou na začátku.
4. Útočník chytne odpověď a zahodí první 4kB dat. Má 60kB heapu ze serveru.
5. S každým opakováním může získat jiný kus RAMky.
No a v těch blocích za alokovaným bufferem už je to loterie, co se v nich najde. Může to být vyplněný nějakou neutrální hodnotou (třeba 0xdeadbeef jak doporučuje McConnel), náhodná hodnota, nebo tam může být přihlašovací paket jinýho uživatele, ketrý čeká na parsování. Útočník dostane blok binárních dat a musí je ještě parsovat, aby zjistil, co vlastně (ne)má. A pokud chce třeba 1MB RAMky, musí dotaz opakovat a nějak si poskládat image RAM. Což chvíli trvá a RAM se při tom dynamicky mění... Takže kumšt není vytáhnout 60kB RAM, kumšt je umět rozpoznat, co přišlo a něco s tím podniknout. A štěstí je rovnou chytnost data, který potřebuje.
Každopádně nevím, jestli je to víc chyba, nebo ostuda.