Pokročilé techniky XSS

Zdroj: SOOM.cz [ISSN 1804-7270]
Autor: .cCuMiNn.
Datum: 23.5.2008
Hodnocení/Hlasovalo: 1.75/80

O zranitelnosti Cross-Site Scripting, která se zkráceně označuje jako XSS, toho bylo napsáno již mnoho. Přesto se s touto zranitelností můžeme stále setkat ve více než 80% webových aplikací. Zkusíme spolu poodhalit důvod, proč tomu tak je a uvedeme si příklady zneužití této zranitelnosti, které by neměly nechat klidným žádného z vývojářů webových aplikací.

Se zranitelností XSS se dnes můžete stále setkat ve více než osmdesáti procentech webových aplikací a to i přesto, že je tato zranitelnost známa již mnoho let. Hned v úvodu si proto položíme otázku, proč je XSS stále tak rozšířené, když je k dispozici velké množství informací, které tuto zranitelnost popisují. V žádném případě si nemyslím, že by o ní snad vývojáři nikdy neslyšeli. Přeci jen je poslední dobou vidět značný pokrok v této oblasti, kdy se vývojáři snaží o vymýcení této zranitelnosti ze svých stránek. Zaměřují se však převážně na persistentní, nebo-li trvalé XSS, které bývalo častým zdrojem nepříjemných „defaců“ webových stránek. V současnosti se střetáváme hlavně s non-persistentními útoky XSS, které často zůstávají před správci webových aplikací skryty. Na výše položenou otázku se proto nabízí následující odpověď: Vývojáři o náchylnosti své aplikace na útoky XSS neví a dozvídají se o ní teprve ve chvíli, kdy jsou na tuto skutečnost upozorněni někým z řad witehatů. Ani po upozornění na přítomnost XSS v aplikaci však její správci často neučiní patřičné kroky vedoucí k odstranění chyby. Musíme si tedy klást další otázku: Proč správci webových aplikací nesjednají nápravu, když o přítomnosti náchylného místa na non-persitentní XSS vědí? Zde je nasnadě také jedna odpověď, která toto jednání vysvětluje tím, že si správci aplikací neuvědomují rizika, která z útoků XSS plynou. Domnívají se, že je možné využít útoky pouze k neškodným hrátkám útočníků, kteří mohou v nejhorším případě ukrást cookie své oběti.

Co byste měli znát

V tomto článku si proto ukážeme příklady využití útoku XSS, které mohou vést k odcizení uživatelského účtu, k plnému ovládnutí webového browseru oběti, k vedení útoků na jiné weby nebo k vytvoření červa, který se sám bude šířit Internetem skrz existující XSS zranitelnosti. Článek si tedy klade za cíl poukázat na téměř neomezené možnosti XSS útoků, které, pokud nebudou z Internetu odstraněny, mohou se do budoucna stát skutečně velikou hrozbou.

Co se naučíte…

Dříve, než se společně podíváme na popis samotného Cros-Site Scriptingu a konkrétní ukázkové útoky, musím seznámit případné začátečníky se skriptovacím jazykem JavaScript a objektovým modelem dokumentu. V případě, že jste s těmito oblastmi obeznámeni, můžete první odstavce přeskočit a začíst se až do samotných informací o XSS.

Skriptování na straně klienta

Na počátku byla potřeba oživení webových stránek, které před implementací skriptovacích jazyků uměly pouze zobrazovat statické informace. Po jejich zavedení dostali vývojáři k dispozici prostředek, který jim umožňoval zobrazovat na webových stránkách aktuální datum a čas nebo přistupovat k jednotlivým objektům na stránce a dynamicky měnit jejich obsah. Jako první přišla se svou implementací v podobě jazyka JavaScript společnost Netscape. Její Javascript byl následně implementován i do Internet Exploreru. Společnost Microsoft však přišla i se svým řešením skriptování na straně klienta v podobě VBScriptu. Ten však nebyl implementován do ostatních prohlížečů a zůstal tak výsadou pouze Internet Exploreru. Existují i různé další méně rozšířené skriptovací jazyky, ale vývojáři ze zřejmého důvodu produkují své kódy převážně v JavaScriptu. Ze stejného důvodu jej ke svým příkladům v tomto článku budu používat i já.

Skripty, které se mají vykonat na straně klienta, jsou do webové stránky vkládány třemi způsoby. Prvním z nich je vložení skriptu a definic funkcí mezi tagy <SCRIPT> a </SCRIPT> do hlavičky dokumentu nebo jeho těla. Prohlížeč pak vykoná kód mezi těmito tagy okamžitě, jakmile jej načte z webového serveru, přičemž další zobrazení obsahu stránky provede až po dokončení běhu skriptu. Jak může vypadat takto napsaný skript je ukázáno ve výpise 1.

Parametr TYPE uvedený u tagu SCRIPT by se sice měl uvádět, ale pokud jej vynecháme, nemusíme se příliš bát, že by to mělo nějaký vliv na funkčnost našeho skriptu. Dalšího zjednodušení pak můžeme dosáhnout, když ze skriptu vypustíme značky pro html komentáře. Ty jsou uvedeny pouze pro případ, že použitý webový prohlížeč nepodporuje skriptovací jazyky a kód skriptu by se pak vypsal jako běžný text v obsahu stránky. Po zjednodušení vypadá stejný skript tak, jak uvádím ve výpise 2. Stejného zjednodušení budu používat i u všech dalších mnou uváděných příkladů.

Druhá z možností, jak do stránky vložit JavaScriptový kód, je jeho načtení z externího souboru. Tag SCRIPT obsahuje pro tento případ parametr SRC (source), který se zapisuje ve tvaru, jenž můžete vidět ve výpisu 3. I v tomto případě se nejprve zobrazí obsah stránky nad tímto tagem, poté se JavaScriptový kód načte a vykoná a teprve poté je zobrazen zbývající obsah stránky. Tento způsob inkludování skriptů se používá většinou při vkládání různých knihoven funkcí, které jsou volány na různých místech webu nebo v případech, kdy je kód natolik rozsáhlý, že by zbytečně činil zdrojový kód stránky nepřehledným.

Třetí a poslední variantou vkládání skriptu do webové stránky jsou takzvané In-line skripty, které se vkládají jako atributy událostí. Tyto skripty se vykonávají jako reakce na určitou činnost uživatele, kterou může být například stisknutí tlačítka na klávesnici, kliknutí myší na objekt, přejetí kurzorem nad objektem, a podobně. Výpis 4 ukazuje způsob zápisu takto použitého skriptu.

Výpis 4 Ukázka In-line skriptu

Domain.com

Tímto jsme vyčerpali všechny možnosti, kterými vývojáři vkládají do stránek kódy svých skriptů. O něco níže, až si budeme představovat útoky XSS, se k těmto metodám opět vrátíme a ukážeme si, jakým způsobem mohou být zneužité ze strany útočníka. Poslední informací, kterou na tomto místě ještě uvedu, je skutečnost, že skriptovací jazyky na straně klienta neumožňují (s vyjímkou souborů cookies nebo exploitů zneužívajících chyby ve webovém prohlížeči) přistupovat k souborům na disku. Je tak zabráněno tomu, aby útočník mohl na disk ukládat nebezpečné soubory nebo z disku číst soukromé informace. Tato skutečnost je asi největším omezením zmíněných skriptovacích jazyků. Bez něj by ale byl uživatel na Internetu naprosto bezbranný a stal by se tak pro útočníka velice snadným cílem.

Document Object Model (DOM)

Pracujeme-li se skriptovacím jazykem na straně klienta, budeme často přistupovat k různým objektům v dokumentu. Když mluvím o objektech, myslím tím jednotlivá otevřená okna prohlížeče, odstavce textu, vložené rámy, obrázky, formuláře, tlačítka a vůbec všechny prvky, které se v dokumentu nachází. Již jsme si řekli, že pomocí JavaScriptu můžeme k těmto objektům libovolně přistupovat. Můžeme číst jejich obsah nebo jej dokonce i měnit. Díky tomu mohou vývojáři tvořit dynamicky měněné stránky, které téměř okamžitě reagují na akce uživatelů. Objektový model má svou jasně stanovenou hierarchii, kde na nejvyšším stupni stojí objekt Window, který zastupuje okno prohlížeče, tento objekt je následován objekty typu document, které tvoří jednotlivé načtené dokumenty. Následují jednotlivé objekty v dokumentu a jejich další dceřiné objekty. Vždy, když je potřeba přistoupit k některému objektu, je potřeba se k němu prokousat celou touto hierarchií. K vlastnostem jednotlivých objektů pak přistupujeme skrz jejich metody. Je potřeba se zmínit, že ne všechny prohlížeče mají DOM implementován shodně. Některé prohlížeče jej mají implementován pouze částečně a některé dokonce vůbec. Nejlépe na tom jsou prohlížeče od Microsoftu a Mozilly. I mezi jejich implementací však existují určité rozdíly, na které je třeba myslet a tak je často nutné vytvářet pro každý z těchto browserů rozdílné kódy. Poslední věcí, kterou bych na tomto místě rád zmínil, je bezpečnostní omezení zabraňující přistupovat k objektům, které jsou umístěny na stránce pocházející z jiné domény. Toto omezení se označuje výrazem Same Origin Policy. Jeho existence je pro bezpečnost natolik důležitá, že nebude od věci se u ní chvíli pozastavit.

Same Origin Policy

Již jsme zmínili, že toto omezení zabrání přistupovat skriptem z jedné domény k objektům, které leží v doméně jiné. Není to však omezení jediné. Abychom mohli k objektům přistupovat musí se shodovat nejenom doména, server, použitý protokol, ale i port, přes který komunikujeme. V tabulce 1 uvádím pár příkladů, kdy se nám přístup k objektům podaří a kdy nikoliv. Vše si ještě navíc popíšeme na praktickém příkladu.

Řekněme, že máme otevřeny dva dokumenty. Každý z nich může být otevřen v jiném okně, nebo jeden tvoří stránku se skriptem a s vloženým rámem a druhý dokument je obsahem rámu, který je do předchozí stránky vložen. Pokud by byl dokument se skriptem umístěn na serveru útočníka, a ten druhý by pocházel z důvěryhodného serveru, bylo by asi dost nepříjemné, pokud by útočníkův skript mohl měnit hodnoty objektů na důvěryhodné stránce, nebo kdyby umožňoval číst jejich hodnoty například v podobě cookies. Sami vidíte, že pokud by byla taková akce útočníkovi povolena, jednalo by se o naprosto fatální bezpečnostní problém. Útočníkovi se díky omezení Same Origin Policy naštěstí nemohou podobné útoky nikdy podařit. K tomu, aby mohl podobný útok provést, musel by své skripty umístit ve stejné doméně, na kterou útočí. No a to už se pomalu dostáváme k jádru útoků XSS, pomocí níž je právě umístění útočníkových skriptů do napadených webových aplikací možné.

Tabulka 1 Same Origin Policy - kam má útočník prostřednictvím skriptu umístěného na stránce http:\\www.domain.com\atackscript.html přístup a na která místa přístup nemá.

http:\\www.bank.com\account.html NE Liší se doména
http:\\www.domain.edu\account.html NE Liší se doména
http:\\faq.domain.com\ask.html NE Liší se server
ftp:\\www.domain.com NE Liší se protokol
http:\\www.domain.com\info.html:8080 NE Liší se port
http:\\www.domain.com\info.html ANO Údaje souhlasí
http:\\www.domain.com\account\info.html ANO Údaje souhlasí

Dobrá znalost JavaScriptu a objektového modelu jsou pro útočníka předpokladem pro sofistikované Cross-Site Scripting útoky. S jejich znalostí je útočník při plánování útoků omezen pouze svou vlastní fantazií a možnostmi, které mu tyto nástroje povolují. Pokud si chcete utvořit jasnější představu o možnostech XSS útoků, neměli byste také znalosti těchto dvou nástrojů podceňovat. Bez jejich pochopení, můžete jen s velkými obtížemi stavět barikády, které mají vetřelcům zabránit v provádění útoků tohoto druhu.

Úvod do Cross-Site Scripting (XSS)

Výraz Cross-Site Scripting, který se zkráceně označuje jako XSS nebo někdy také CSS (neplést s kaskádovými styly), v překladu znamená skriptování napříč sítěmi a jak jsem zmínil již v úvodu, jedná se o jednu z nejvíce rozšířených zranitelností současného webu. Se zranitelností XSS se můžeme setkat v několika podobách, přičemž nejčastěji je dělíme na trvalé (persistentní) XSS a na dočasné (Non-persistentní) XSS. Setkat se však můžeme také s DOM-based XSS nebo Self-contained XSS. Myslím, že právě nyní je ten správný čas, abychom si o každém z nich řekli něco bližšího a konečně si také zranitelnost XSS vysvětlili.

Persistentni (trvalé) XSS

Zranitelnost tohoto typu je nejsnáze pochopitelným druhem XSS a proto začneme v našem popisu právě jím. Setkáme se s ním hlavně v diskusních fórech, návštěvních knihách, komentářích ke článkům a všude tam, kde mají návštěvníci možnost vložit natrvalo svůj příspěvek na webovou stránku. Pokud vstup od návštěvníka není dostatečně kontrolován a upraven logikou na straně serveru, může ze strany návštěvníka dojít ke vložení nebezpečného skriptu, který se spustí ve webovém browseru každému, kdo na stránku, která tento příspěvek zobrazuje, vstoupí. Uvedeme si konkrétní příklad. Uvažujme, že máme na svých webových stránkách návštěvní knihu, která nekontroluje vstupy zadané uživateli. Kód takové knihy můžete vidět ve výpisu 5. Dále budeme uvažovat, že se útočník pokusí o vložení skriptu, který při každém zobrazení příspěvku provede přesměrování návštěvníka na jinou webovou stránku. Takový skript by mohl vypadat podobně, jako je uvedeno ve výpisu 6. Co se nyní stane, když útočník vloží skript z výpisu 6, jako svůj příspěvek do návštěvní knihy? Zdrojový kód webové stránky s příspěvkem pak bude vypadat, jako ten z výpisu 7 a k provedení skriptu dojde vždy, když se návštěvníkovi stránka zobrazí.

" . $text . "
"; fputs($fp, $message); fclose($fp); endif; ?>
Name:
Message:
<textarea name="text" rows=5 cols=60></textarea>

Sami vidíte, že k provedení útoku stačilo opravdu málo a jeho následky mohou být nedozírné - od jednoduchého přesměrování, přes redesign stránky až po naprosté ovládnutí prohlížeče oběti. O konkrétních útocích, ke kterým může být XSS využito si povíme níže. Na druhou stranu, je tento útok ze strany majitele webové aplikace okamžitě zjistitelný a proto tato zranitelnost ze stránek velice rychle mizí.



  
  
Name:
Message:
<textarea name="text" rows=5 cols=60></textarea>
Jack
My message
John
My script in message

In-line JS / Self-contained XSS

Tento druh útoku je také velice jednoduchý. Schválně si zkuste do adresního řádku vložit toto: javascript:alert('XSS'); a svůj vstup odentrovat. Mělo by na Vás vyskočit výstražné okno s textem XSS, které je výsledkem námi vloženého skriptu přes adresní řádek. Pokud máme k dispozici návštěvní knihu, do které můžeme vkládat odkazy, pak je nám často umožněno vložit na stránky i takto připravený skript. Ukázku vstupu pro tento útok uvádím ve výpise 9. Jakmile oběť klikne na takto připravený odkaz, spustí se v kontextu dané stránky uvedený skript, stejně jako by ho oběť sama zapsala do adresního řádku.

http://www.searchsite.com/search.php?query=%3Cscript%3Ealert%28%27XSS%27%29%3B%3C%2Fscript%3E


Odkaz

Stejný útok se dá provést ještě druhým způsobem, který funguje v prohlížečích FireFox nebo Opera, a který umožňuje zapsat stejný skript způsobem, který uvádím ve výpisu 10. Více se o tomto způsobu kódování rozepíši v odstavci věnovaném skrývání skriptů před odhalením.

Výpis 10 Útočníkův odkaz pro self-contained XSS zakódovaný pomocí Base64

Odkaz

Non-persistentní XSS

S vysvětlením tohoto typu zranitelnosti to již nebudu mít tak jednoduché jako v předchozích případech. U non-persistentního XSS dochází k vykonání kódu, který je předán jako součást požadavku na stránku. Parametr je na straně serveru zakomponován do webové stránky, která je následně zobrazena uživateli. S touto zranitelností se nejčastěji setkáme u různých vyhledávačů, stránek s chybovým hlášením (například s kódem 404), či na stránkách, které si předávají obsahy zobrazitelných zpráv v parametrech URI. Pokusím se opět uvést konkrétní případ, při němž se můžeme s non-persistentním XSS střetnout.


  
    Search Results
  
  
    
Your search for example xss atack in DRD found 0 hits. No results were found for your search in DRD.

Uvažujme, že máme vyhledávací nástroj, který prochází obsah webu a zobrazuje nalezené výsledky. Při každé odpovědi navíc server odpoví zprávou: Na hledaný výraz XYZ bylo nalezeno 10 odpovědí (viz. obrázek 1). Zjednodušený zdrojový kód takové zobrazené stránky by mohl vypadat podobně, jak ukazuje výpis 11. Co se nyní stane, když útočník vloží dotaz na výraz, který obsahuje kód skriptu uvedeného ve výpisu 12. Po odeslání požadavku na hledání je hledaný řetězec předán jako parametr vyhledávacímu skriptu a následně je zobrazena stránka, která obsahuje námi injektovaný skript. Ten se pochopitelně okamžitě po načtení vykoná (viz. obrázek 2). Zdrojový kód stránky s injektovaným skriptem můžete vidět ve výpisu 13, přičemž URI výsledné stránky má tvar, který uvádím ve výpisu 8. V uvedeném odkazu je hledaný výraz zakódován pomocí URL kódování. Nyní již stačí pouze zaslat tento odkaz oběti, které se skript spustí, jakmile na odkaz klikne.



  
    Search Results
  
  
    
">
Your search for in DRD found 0 hits. No results were found for your search in DRD.

Obrázek 1 Ukázka odpovědi od webového vyhledávače

Obrázek 2 Úspěšný útok přes webový vyhledávač

Bypassing

V určitých případech dochází u non-persistentního XSS k výpisu útočníkem zadaného skriptu tak, že se stane hodnotou parametru nějakého jiného tagu. V takovém případě by se skript nevykonal a je proto nutné, aby útočník zmíněný tag nejprve uzavřel. Nejlépe uděláme, pokud si uvedené informace opět přiblížíme pomocí konkrétního příkladu.


Pro vstup do zbývající části webu se musíte přihlásit:

Username: Password:

Uvažujme, že máme stránku, která obsahuje přihlašovací formulář k uživatelskému účtu. Do toho se běžně zadává přístupové jméno a heslo a v případě, kdy je zadána nesprávná kombinace přístupových údajů, je tento přihlašovací formulář opětovně zobrazen pro vyplnění. Při druhém pokusu je však již předvyplněna hodnota pole s uživatelským jménem, které jsme vložili při prvním pokusu. Pokud nejsou hodnoty z přihlašovacího formuláře kontrolovány, může dojít ke spuštění našeho skriptu. Ve výpisu 14 je uveden zdrojový kód stránky s přihlašovacím formulářem a ve výpisu 15 pak zdrojový kód téže stránky po té, co jsme do pole pro uživatelské jméno vložili skript z výpisu 12. Pomocí barevně zvýrazněné syntaxe můžete vysledovat, co se stalo.

Výpis 15 Zdrojový kód stránky s předvyplněným uživatelským jménem, místo kterého byl použit útočníkův skript

Útočníkův skript se vložil jako řetězec do vstupního pole formuláře, a při zobrazování stránky se proto nespustil. Pokud bychom ale předchozí řetězec a tag nejprve uzavřeli pomocí kombinace znaků "> a teprve poté uvedli náš skript, byla by situace jiná a ke spuštění kódu by již došlo. Ve chvíli, kdy si nejsme jisti, zda tvůrce aplikace použil v tagu uvozovky nebo apostrofy, vyplatí se vytvořit bypass pomocí této kombinace znaků '">, která by řetězec a tag uzavřela v každém případě. I nadále má však provedený útok jeden nedostatek a to ten, že za vstupním polem nám zůstaly dva nevyužité znaky v podobě ">, které budou na webové stránce zobrazeny. Abychom tomu zabránili, vložíme za náš skript ještě znaky pro html komentář, které se postarají o jejich skrytí. Výsledný skript, který vložíme do vstupního pole pro uživatelské jméno, bude tedy nakonec vypadat tak, jak je uvedeno ve výpisu 16. Po tomto útoku vypadá zdrojový kód stránky stejně jako ten z výpisu 17.

'">