V prvním díle minisérie věnované Kontrole uživatelských účtů jste se dozvěděli, jak tento mechanismus funguje a jaké má pro oprávnění běžících aplikací důsledky. Druhý, a současně poslední, díl pojednává o tom, jak tento mechanismus obejít. Na ukýzkový zdrojový kód rovněž nebylo zapomenuto.
Implementaci Kontroly uživatelských účtů ve Windows Vista by mnohý uživatel jistě eufemisticky označil za „neskutečně otravnou“. UAC dialog totiž vyskakoval prakticky při každé změně nastavení systému, takže například výlet do Ovládacích panelů se stával časově náročnějším než v předchozích verzích Windows. Mnoho uživatelů se na základě této zkušenosti určitě rozhodlo UAC úplně vypnout (což prakticky znamená návrat do stavu z předchozích verzí operačního systému).
Windows 7 v tomto ohledu přinesla pozitivní zkušenost, protože při změně systémových nastavení zobrazovala UAC dialog v minimálním množství případů. Důvod tohoto chování pravděpodobně představuje zavedení speciálního seznamu procesů, které jsou spouštěny s administrátorskými právy, aniž by prošly přes UAC dialog (platí pouze pro případ, že příslušný uživatel je správcem). Většina takových procesů se nachází v systémovém adresáři nebo v jeho podstromě. Bohužel, některé z nich nemají dostatečně ošetřeno načítání DLL knihoven, které ke svému běhu potřebují. Tudíž se nabízí otázka, zda-li by jim nešllo podstrčit DLL knihovnu upravenou útočníkem. A odpověď na tuto otázku už přes pět let zní ANO.
Než ale bude možné přistoupit ke zdůvodnění této odpovědi, je nutné absolvovat další obecnou vsuvku, tentokráte o spustitelných souborech ve Windows.
Spustitelné soubory
Většina druhů spustitelných souborů ve Windows (EXE, DLL knihovny, ovladače) dodržuje jednotný binární formát s názvem Portable Executable (PE). Zjednodušeně řečeno, každý spustitelný soubor obsahuje následující:
- kód v podobě souboru funkcí a procedur,
- data (globální proměnné a konstanty),
- seznam exportovaných symbolů (funkcí, procedur, proměnných), které poskytuje vnějšímu světu k volnému užití (využívají jej zejména DLL knihovny a některé druhy ovladačů),
- seznam importovaných symbolů (zejména procedur a funkcí) udává, jaké knihovny daný spustitelný soubor ke své činnosti potřebuje a jaké jimi exportované symboly využívá.
Každý symbol exportovaný spustitelným souborem může být identifikován jménem a/nebo číslem (ordinálem). Seznam importovaných symbolů se dělí do „adresářů“, každý z nich obsahuje informace o symbolech importovaných z určité DLL knihovny. Jméno knihovny je uvedeno bez cesty k jejímu souboru, což nechává útočníkům teoretický prostor k podstrčení vlastní knihovny do právě startující (či již běžící) aplikace).
Poznámka: Symboly nemusí exportovat pouze DLL knihovny. Pro jednoduchost se však tento text zaměřuje hlavně na ně.
Aplikace též mohou DLL knihovny načítat a využívat jejich funkcionality dynamicky – například pouze v určitých okamžicích své existence. Před započetím práce DLL knihovnu načtou do paměti, po dokončení úkolu ji zase uvolní. I v případě takového explicitního načítání je zvykem specifikovat pouze jméno souboru knihovny, nikoliv celou cestu.
Systém tedy musí být schopný nalézt požadovanou knihovnu na základě jména jejího souboru, aniž by znal jeho přesné umístění. Postupně prohledává určité složky, dokud nenarazí na soubor požadovaného jména. Ten pak považuje za správnou knihovnu a načte jej do paměti. Proces vyhledávání správného souboru probíhá nezávisle na tom, zda je knihovna požadována, protože jiný spustitelný soubor z ní importuje symboly, nebo zda je její načtení vyžádáno explicitně aplikací.
Tabulka 2 ve svých řádcích popisuje jednotlivá místa, kde systém hledá při načítání DLL knihoven do paměti. Číselné údaje v jednotlivých řádcích udávají pořadí prohledávání pro nastavení uvedená v příslušném sloupci.
Pokud zadané jméno souboru knihovny patří mezi tzv. známé knihovny (KnownDlls), k prohledávání nedochází a je namapovaná kopie příslušné známé knihovny, která obvykle sídlí v systémovém adresuáří.
Poznámka: Seznam známých knihoven se nachází v klíči registru HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerKnownDlls. Pro 32bitové aplikace na 64bitových verzích Windows je ze stejného důvodu důležitý klíč KnownDlls32 ve stejném podstromě.
Tabulka 2: Pořadí prohledávaných umístění při načítání DLL knihoven
Standardní prohledávání | Alternativní prohledávání | |||
---|---|---|---|---|
DllSearchMode | ||||
Zapnuto | Vypnuto | Zapnuto | Vypnuto | |
Složka aplikace | 1 | 1 | – | – |
Systémový adresář | 2 | 3 | 2 | 3 |
Sys. adresář (16bit) | 3 | 4 | 3 | 4 |
Adresář Windows | 4 | 5 | 4 | 5 |
Aktuální adresář | 5 | 2 | 5 | 2 |
%PATH% | 6 | 6 | 6 | 6 |
Adresář knihovny | – | – | 1 | 1 |
Poznámka: Alternativní prohledávání se použije v případě, že při volání funkce LoadLibraryEx specifikujete absolutní cestu ke knihovně, kteoru chcete načíst, a dále nastavíte příznak LOAD_WITH_ALTERED_SEARCH_PATH. Alternativní prohledávání se aplikuje na všechny další moduly, ze kterých daná knihovna importuje, a to i nepřímo (moduly, ze kterých importují moduly exportující pro danou knihovnu atd.). V případě načítání těchto modulů nejprve dcohází k prohleádávní adresáře s danou knihovnou.
Další možnost obrany představuje uvedení explicitních cest k DLL knihovnám do manifestu daného EXE souboru. Systém tedy ví, kde knihovny daných jmen hledat, a tak k prohledávání nedochází. Jako příklad poslouží úryvek manifestu procesu sysprep.exe na Windows 8.1.
<!-- Specifically load these DLLs from the specified path. This is done as a defence-in-depth approach to closing a known UAC exploit related to Sysprep.exe being auto-elevated. The list need not contain KnownDlls since those are always loaded by the loader from the system directory. --> <file loadFrom="%systemroot%system32actionqueue.dll" name="actionqueue.dll" /> <file loadFrom="%systemroot%system32bcryptprimitives.dll" name="bcryptprimitives.dll" /> <file loadFrom="%systemroot%system32cryptbase.dll" name="cryptbase.dll" /> <file loadFrom="%systemroot%system32unattend.dll" name="unattend.dll" /> <file loadFrom="%systemroot%system32wdscore.dll" name="wdscore.dll" />
Jak ale demonstruje praktická ukázka v závěru tohoto textu, na některé knihovny se v manifestu této utility zapomnělo.
Pořadí prohledávaných oblastí také ovlivňuje nstavení registrové hodnoty DllSearchMode. Hodnota má typ REG_DWORD a obsahuje-li nenulová data, pro prohledávání platí první a třetí sloupeček tabulky (sloupeček hlavičky se nepočítá).
Z tabulky 2 je patrné, že při načítání DLL knihovny systém začíná hledat v adresáři s EXE souborem aplikace, v jejímž kontextu k načítání dochází. Tato složka je preferována dokonce i před systémovým adresářem či adresářem Windows. Pokud se útočníkovi podaří nakopírovat vlastní knihovnu s příhodným jménem do adresáře aplikace, která prochází přes UAC dialog automaticky, stačí mu tuto aplikaci následně spustit a má vyhráno.
Plán útoku
Postup útočníka může tedy vypadat následovně:
- Vyhlédnutí obětního beránka. Nejprve je třeba najít aplikaci, která automaticky prochází přes UAC dialog a při načítání knihoven dochází k prohledávání umístění popsaných v tabulce 2. Seznamy aplikací splňující první kritérium lze pravděpodobně nalézt na internetu, případně je možné uchýlit se ke kvalifikovanému odhadu. Většina takových aplikací totiž sídlí v systémovém adresáři či v jeho podstromu.
- Určit název útočící knihovny. Po spuštění vyhlédnuté aplikace se útočník podívá, jaké knihovny používá (např. pomocí nástroje Process Explorer) a vybere si některé ze jemn nepatřící žádné ze známých knihoven.
- Nakopírovat útočící knihovnu k vybrané aplikaci. Je pravda, že snad všechni potenciální obětní beránci sídlí v systémovém adresáři či jeho podstromu, tedy na místech, kam běžný uživatel nemá právo zápisu. Zde útočníkovi nahrává další zvláštní vlastnost – některé procesy (například Průzkumník) se za využití metod COM objektu IFileOperation mohou dopustit kopírování do těchto zakázaných oblastí, aniž by jim byl odepřen přístup. Mohou vytvářet nové soubory, ale ne měnit existující. Útočník tedy musí donutit některý z takto privilegovaných procesů nakopírovat útočící knihovnu na příslušné místo. V případě Průzkumníka stačí obyčejná injekce kódu (ať už surového nebo v podobě DLL knihovny).
- Spuštění obětního beránka. Tento krok by neměl představovat problém, jelikož oprávnění pro čtení a spouštění souborů má i běžný uživatel prakticky všude (jednu z výjimek tvoří profily jiných uživatelů).
- Slavení úspěchu. Pokud se útočník při provádění kroků (1) a (2) nezmýlil, spuštěný obětní beránek načte útočící knihovnu, jejíž inicializační rutina se volá s jeho oprávněním, tedy opravdovými administrátorským.
Praktické ukázky
Součástí tohoto textu je i zdrojový kód projektu demonstrujícího obejití Kontroly uživatelských účtů. Projekt se nese poněkud neoriginální jméno UACBypass a skládá se ze dvou částí: programu Injector zodpovědného za vložení zadané DLL knihovny do určitého běžícího procesu (v tomto případě je použit k vložení útočné DLL knihovny do Průzkumníka Windows) a z útočné DLL knihovny, jež provádní vlastní algoritmus obcházení UAC.
Jedná se o klasickou metodu popsanou v roce 2009 Leo Davidsonem, zdrojový kód UACBypass je ale v porovnání s tím Leovým asi šestkrát menší a pravděpodobně i přehlednější. Příčina spočívá v tom, že Leo do Průzkumníka nevkládá DLL knihovnu, ale pouze kód a data potřebné k obejití UAC, což je sice méně viditelné, ale může to pro některé zatemnit hlavní smysl ukázky.
Útočná knihovna je do Průzkumníka vkládána tak, že dojde k vytvoření nového vlákna v tomto procesu. Startovní adresa nového vlákna se shoduje s počátkem funkce LoadLibraryW v knihovně kernel32.dll a parametrem je adresa plného jména souboru útočné knihovny. Toto jméno je před samotným vytvořením vlákna do Průzkumníika nakopírováno. Program Injector předpokládá, že virtuální adresa knihovny kernel32.dll bude v obou procesech (tedy v Injectoru a v Průzkumníku) stejná.
Samotná DLL knihovna pracuje následovně:
- Zjistí, zda disponuje administrátorskými právy.
- Pokud ne, nakopíruje se do adresáře zadaného ve zdrojovém kódu. Následně spustí proces obětního beránka, který je též specifikován ve zdrojovém kódu, a počká na jeho ukončení. Předpokládá, že obětní beránek načte novou instanci útočné knihovny, tentokrát již ale s právy správce.
- Pokud ano, spustí Příkazový řádek, který zdědí administrátorská práva od obětního beránka. Potom beránka ukončí.
Výsledkem úspěšného útoku je tedy nová instance Příkazového řádku běžící s oprávněním správce. Pro otestování, zda útok opravdu funguje, můžete vyzkoušt následující kombinace názvu útočné knihovny a obětního beránka:
- shcore.dll a %windir%system32sysprepsysprep.exe (Windows 7 – Windows 8.1),
- NTWDBLIB.dll a %windir%system32cliconfig.exe (Windows 7 – Windows 10 TP 9996).
V prvním případě útok zafunguje pouze v případě, že máte povolené animace grafického uživatelského rozhraní. Ty jsou v klientských verzích Windows ve výchozím nstavení zapnuté, na serverech je tomu opačně. Zároveň je nutné po spuštění obětního beránka kliknout na některý z comboboxů.
Druhý popisovaný případ funguje bez problémů.