Autor: .cCuMiNn. | 19.5.2014 |
Jak již název tohoto útoku napovídá, bude při něm využito zkracování. Konkrétně zkracování řetězců, které jsou delší než maximální délka konkrétního datového typu, případně delší než délka uvedená v definici sloupce. Předpokládám, že s návrhem databáze máte již určité zkušenosti a například typ varchar, na který se v následujícím textu zaměřím, vám není zcela neznámý. Tento typ se používá převážně tam, kde je známá maximální délka ukládaných řetězců. Často se tak s typem varchar setkáte například pro ukládání hodnot PSČ, telefonních čísel, IČ, DIČ, nebo třeba hashů hesel. Vlastností varcharu, ale nejen jeho (ve větším rozsahu se to týká i ostatních textových typů, viz. následující tabulka), je, že při ukládání řetězce delšího než definovaná velikost, ořeže SQL server ukládaný řetězec a uloží pouze jeho úvodní část, která se do sloupce konkrétního typu vejde. Zbývající část řetězce je jednoduše zahozena.
Dříve než vám popíši, jak bylo možné pomocí zranitelnosti SQL Truncation hacknout uživatelské účty na našem serveru SOOM.cz, pojďme si nejdříve uvedené údaje a jejich možný dopad na prolomení zabezpečení přiblížit na aplikaci, která v tabulce s uživatelskými účty obsahuje sloupec login typu varchar(20). Řekněme si také, že tato aplikace neumožní registraci dvou uživatelských účtů se shodným loginem, což je logické.
Jako pískoviště, kde si vše můžete v praxi vyzkoušet, nám jako už mnohokrát poslouží náš zranitelný webmail. Konkrétně obsahuje registrace do našeho webmailu kód podobný následujícímu (upozorňuji, že je v uvedeném kódu i mnoho dalších zranitelností, takže tento kód rozhodně nekopírujte do svých vlastních aplikací).
Nyní budeme předpokládat, že existuje uživatelský účet s loginem „admin“, ke kterému bychom se chtěli přihlásit pomocí SQL Truncation tak, že si založíme svůj vlastní účet se shodným loginem. Jak toho ale dosáhnout, když je z výše uvedeného kódu patrné, že probíhá kontrola, zda již účet se shodným loginem náhodou neexistuje? Je to jednoduché a využijeme k tomu právě vlastnost osekání dlouhých řetězců na straně SQL serveru.
Abychom zjistili, jak dlouhý login můžeme použít. Zaregistrujeme se s nějakým hodně dlouhým loginem například „totojemujhodneprehodnedlouhylogin“, který má 33 znaků a následně se k tomuto účtu zkusíme přihlásit. Nemělo by se nám to podařit, protože jak již víme, uložila se do tabulky users hodnota dlouhá pouze 20znaků, tedy „totojemujhodneprehod“. Zkoušíme tedy postupné přihlašování pomocí stejného hesla a stále se zkracujícího loginu. Ve chvíli, kdy se nám podaří přihlášení k našemu účtu, budeme vědět, na jakou délku byl řetězec osekán.
Nyní zjištěné skutečnosti využijeme k tomu, abychom tímto způsobem založili duplicití účet s loginem „admin“. Zaregistrujeme se tedy s loginem, který bude začínat požadovaným řetězcem „admin“ následovaným alespoň 15-ti mezerami a na konci s libovolnými znaky, které budou při uložení odseknuty. Podoba takového loginu by mohla být například tato: „admin abc“.
Registrace uvedeného loginu projde, protože kontrolou se zjistí, že neexistuje účet, kde by se login rovnal řetězci „admin abc“. Do databáze se ovšem uloží pouze oseknutá verze tohoto řetězce, tedy „admin“ a 15 mezer. Je patrné, že řetězece „admin“ a „admin “ s patnácti mezerami, jsou dvě rozdílné sekvence, které se vzájemně nerovnají, což je evidentně pravda. Neplatí to ale tak úplně v databázích, kde MySQL bude v případě následujícího dotazu zcela ignorovat koncové mezery a v řetězci a na tento dotaz vrátí oba dva uživatelské účty (bez mezer i s nimi).
Co se stane nyní, je již otázkou dalšího návrhu aplikace. Například náš zranitelný webmail autentizuje uživatele kódem podobným následujícímu, který nejprve zjistí, zda uživatel zadal správnou kombinaci jména a hesla, a následně podle loginu přiřadí patřičná oprávnění.
Vzhledem k tomu, že je celý přihlašovací process rozdělen do dvou kroků, stane se, že při přidělování oprávnění na základě loginu budou přidělena oprávnění jiného uživatele se stejným loginem, který byl nalezen jako první, tady „admin” a náš nově založený účet tedy získá jeho administrátorská práva.
Na našem serveru SOOM.cz byla situace podobná. I když jsme měli implementovány kontroly na nemožnost registrace dvou účtů se shodným loginem, nebylo bohužel nasazeno omezení, které by omezovalo délku loginu při registraci. Databázový sloupec pro login byl v našem případě typu text a umožňoval proto uchovávat loginy dlouhé až 65535 znaků. Stačilo se ale zaregistrovat s loginem „admin” následovaným šedesátipěti tisíci mezer a libovolným znakem na konci a registrace se zdařila.
Ověřovací process byl u nás samozřejmě odlišný od výše uvedených autentizačních kódů a tak k podobnému převzetí práv tímto způsobem naštěstí dojít nemohlo. Uživatel s duplicitním loginem se totiž nikdy nemohl ke svému novému účtu přihlásit, protože byla vždy vyžadována znalost hesla prvního z účtů. Co ovšem bylo možné, a kde se zranitelnosti SQL Truncation dalo zneužít, bylo zaslání náhradního hesla po jeho zapomenutí.
Na SQL Truncation byly náchylné také například diskuze a komentáře, kdy neregistrovaný uživatel nemohl použít registrovaný nick. V případě, že by neregistrovaný uživatel použil registrovaný nick následovaný opět tisícemi mezer a nějakého toho postfixu, pak by kontrolou prošel, ale do databáze by se nick uložil bez postfixu. Snad se mi podařilo všechna kritická místa ošetřit...
Obrana před útoky SQL Truncation spočívá například v osekání řetězců obdržených od uživatelů a teprve následných kontrol. V našem případě by tedy při registraci došlo k osekání znaků za hraniční délkou loginu, tedy za mezarami, následně by se odstranily bílé znaky na konci řetězce a teprve po té by se kontrolovalo, zda již stejný nick v databázi existuje.
Druhou variantou je použití operátoru LIKE namísto rovnítka v SQL dotazech, které při porovnání stejných řetězců s a bez koncových mezer vrátí false. Tímto řešením se ale vystavíte dalším problémům při vstupu se znakem procenta a s oštřováním case positive variant.
Třetí možností je pak použít při SQL dotazech binární porovnání, které problémy s mezerami také čistě vyřeší:
Dalším elegantním a doporučeným postupem je nastavení striktního módu SQL, které znemožní ukládání ořezaných hodnot.
V neposlední řadě pomůže také nastavení unikátního klíče těm sloupcům, kde se vyžaduje ukládání jedinečných hodnot. Unikátní klíč vyloučí také vložení shodných záznamů s mezerami a bez nich.
Co dodat… Nezbývá, než se poučit a počítat i s touto variantou útoku. A co vy? Podělte se v komentářích, jestli i některé z vašich kódů trpěli na SQL Truncation.
Použitý zdroj: http://resources.infosecinstitute.com/sql-truncation-attack