Autor: .cCuMiNn. | 5.5.2014 |
Samotné backdoory můžeme hrubě rozdělit do dvou skupin. Na ty, které umožňují spouštět funkce a příkazy jazyka PHP a na ty, které spuští příkazy operačního systému.
V jazyce PHP existuje několik možností, jak programově spustit předaný PHP kód.
Jazykový konstrukt eval() je tou nejznámější možností, která je pro spouštění PHP kódu dokonce přímo určena. Kód, který se má vykonat, přebírá eval() ve formě řetězce, jako svůj jediný argument.
Díky jednoduchosti, využijeme funkci eval() k uvedení do problemetiky a vysvětlíme si na ní také některé další vychytávky.
Jednoduchý skript, kde eval spouští kód echo "Ahoj";, by mohl vypadat takto:
V případě delšího kódu se jednotlivé příkazy oddělují středníkem, viz.:
Uvedené použití funkce eval je pochopitelně stejně hloupé jako otáčet šroubovákem pomocí kleští a tak je dobré si říci, že spouštěný kód je možné evalu předat i v obsahu proměnné, což už dává trochu větší smysl:
No a když už tedy umíme spustit kód uložený v proměnné, nic nám nebrání v tom, abychom obdobným způsobem spustili také kód přicházející od uživatele ve formě parametru GET požadavku:
Poznánka: Kód samozřejmě nemusí být předaný pouze GETem, ale i POSTem, nebo třeba v COOKIE.
Na tomto kódu si vysvětlíme ještě jednu zásadní věc. Pokud bychom navštívili stránku s parametrem kod omylem a hodnota této proměnné by nebyla validní, mohla by stránka zobrazit při spuštění kódu chybové hlášení, jež by mohlo vést až k odhalení těchto zadních vrátek. Z tohoto důvodu se v backdoorech často používá ještě zavináče @, který zobrazení chybových zpráv potlačuje.
Ve finále by tedy celý backdoor vypadal takto:
No a to je vlastně vše. Jediný řádek @eval($_GET["kod"]); schovaný kdesi v kódu umožňuje útočníkovi, aby kdykoliv v naší aplikaci spustil libovolný příkaz a třeba nám tím celou aplikaci defacenul.
Protože je ovšem funkce eval() natolik známá a její význam naprosto zřejmý, není se čemu divit, že webmasteři často preventivně vyhledávají výskyt tohoto řetězce ve všech svých kódech pomocí automatizovaných nástrojů.
Útočníkům tak nezbývá, než se poohlédnout po jiných možnostech, které nejsou tolik rozšířené a známé, nebo jejichž automatické vyhledání je obtížnější nebo rovnou nemožné.
Dokumentace k eval(): http://www.php.net/manual/en/function.eval.php
Funkce assert sice slouží přimárně k něčemu jinému, než je spouštění kódu, konkrétně k testování pravdivosti výroků. Každopádně pokud je jí předán řetězec, spustí jej jako PHP kód.
Dokumentace k assert(): http://cz2.php.net/manual/en/function.assert.php
Hůře čitelná a hůře pochopitelná již bude funkce preg_replace(). Ta se snaží najít výskyt řetězce odpovídajího regulárnímu výrazu, který před jeho nahrazením díky modifikátoru /e nejprve provede jako PHP kód.
Více o preg_replace: http://interval.cz/clanky/regularni-vyrazy-v-php-pro-zacatecniky-ii/
Další probíraná funkce bude opět hůře čitelná. Funkce array_diff_ukey() používá zpětného volání uživatelské funkce, která provede vyhodnocení předaných argumentů. Pokud tedy použijeme argumenty na funkci předanou parametrem z URL, můžeme tak opět snadno dosáhnout spuštění kódu.
Nevýhodou je, že funkce musí přijímat více než jeden parametr a nejsou akceptovány jazykové konstrukty.
Více o tomto backdooru se dočtete například na následujícím blogu: http://blog.sucuri.net/2014/04/php-callback-functions-another-way-to-hide-backdoors.html
Velice jednoduchý, ale hlavně velmi nenápadný backdoor získáme použitím proměnné k uchování a zavolání konkrétní funkce. V tomto případě stačí, aby jedna proměnná obsahovala název funkce, kterou hodláme zavolat, např. system a druhá proměnná argument, který chceme funkci předat.
Nevýhodou tohoto způsobu pro útočníky je skutečnost, že jej lze využít pouze pro spouštění funkcí (nefunguje pro volání jazykových konstruktů). Na druhou stranu je tento způsob v kódu nejméně nápadný a lze jej různě obfuskovat. Jeho vyhledání pomocí automackých nástrojů je tedy velice složité.
Pokud bychom chtěli více zamaskovat vstupy získané z GET požadavku, mohli bychom u předchozího příkladu použít například funkci extract, jak ukazuje následující příklad. Sama o sobě ovšem funkce nezpůsobuje spuštění kódu, pouze útočníkům umožní zbavit se pouřití proměnné $_GET.
Dokumentace k funkci extract: http://cz2.php.net/manual/en/function.extract.php
PHP kód je možné spustit také s využitím create_function(). Backdoor v tomto případě funguje tak, že se jako tělo funkce použije útočníkův vstup a následně se funkce spustí voláním jejího názvu.
Rozdíly mezi uvedenými funkcemi budeme v našem případě zanedbávat protože pro nás při povídání o backdoorech nehrají roli. Funkci include, kterou uvedu v kódu tohoto backdooru tedy můžete libovolně zaměnit s kteroukoli jinou z uvedené čtveřice.
Nevýhodou je pro útočníky nutnost zapnuté direktivy allow_url_include, což je na serverch spíše výjimečný jev.
Druhou skupinou jsou backdoory umožňující spuštění systémových příkazů. Je potřeba si ale uvědomit, že rozdíl od předchozího typu se stírá skutečností, že i pomocí výše uvedených backdoorů, je možné volat níže uvedené funkce spouštějící externí programy.
Tato čtveřice funkcí je si opět velice podobná a proto i kód backdooru je víceméně shodný s výjimkou názvu volané funkce.
Konkrétní význam jednotlivých fukcí je:
Použití zpětných apostrofů není nic jiného, než alias pro shell_exec(). V kódu se ale zpětná lomítka mnohem lépe ztratí, takže jejich použití ze strany útočníků není vůbec od věci.
Vzhledem k tomu, že se jedná o alias funkce shell_exec(), je možné backtick operátory zablokovat zakázáním této funkce.
Více v dokumentaci: http://www.php.net/manual/en/language.operators.execution.php
Existuje ještě mnoho dalších potenciálně nebezpečných funkcí, které mohou způsobit spuštění kódu, nebo zobrazení obsahu lokálních souborů. Jako doporučené čtení proto doporučuji například tuto diskuzi, kde se stejnému tématu rovněž věnují a kde je uveden například výčet funkcí umožňující zpětné volání.
Poznámka: Mnoho z uvedených funkcí může být na straně serveru zakázáno, takže nespoléhejte na to, že všechny zde uvedené postupy budou vždy spolehlivě funkční. Které funkce jsou zakázány zjistíte prozkoumáním výstupu funkce phpinfo().