Ko smo najmanj pričakovali, je spet udarilo, a tokrat še močneje. Na začetku septembra je Stephane Chazelas odkril varnostno luknjo oziroma ranljivost, za katero je predlagal ime »bashdoor«. Uporabniki *NIX/Linuxa vedo, da že ime samo ne pomeni nič dobrega. Bashdoor ali tudi Shellshock kaže na ranljivost lupine Bash, ki je del praktično vsakega Linuxa/Unixa/OS X-a.

NEODKRIT VEČ KOT DVE DESETLETJI

Kako velika je težava, največ pove nekaj dejstev. Če upoštevamo lestvico CVSS (Common Vulnerability Scoring System), ki sega od 1 do 10 (deset označuje največjo ranljivost sistema), ima HeartBleed vrednost »5«, Shellshock pa »10«. Da je problem še večji, je omenjena ranljivost označena kot razmeroma preprosta za izkoriščanje. To posledično pomeni, da lahko vsak, ki ima korist, razmeroma preprosto napiše zlonamerno kodo, ki lahko izkorišča tovrstno ranljivost. Skoraj neverjetno pa je dejstvo, da je bila napaka v omenjenih sistemih prisotna več kot dve desetletji. Brez ustreznih varnostnih popravkov so zato ranljivosti izpostavljene tako starejše (na primer 1.14) kot tudi novejše različice (na primer 4.3) Bash in seveda vse med njimi.

Če želimo kompleksnost problema razumeti in se ustrezno odzvati ter ukrepati, moramo razumeti njegovo bistvo. Vsi operacijski sistemi imajo različne ukazne lupine (Cshell, Bash, cmd …). Ukazno lupino najpreprostejše opišemo kot neki vmesnik med uporabnikom in operacijskim sistemom, ki se uporablja za zagon drugih programov. Določene lupine omogočajo branje lastnih tekstovnih datotek (skript) in omogočajo druge elemente programskih jezikov, zato delujejo navzven kot svoj programski jezik. Lupina Bash je »standardna« lupina v Linuxu in omogoča dva načina uporabe. Pri prvem (interaktivnem) uporabnik vnese ukaze, pri drugem pa so ukazi shranjeni v obliki tekstovnih datotek. Lupina prebere vsebino skriptne datoteke in obravnava ter izvaja ukaze, kot bi bili vneseni s pomočjo ukazne vrstice. Dobra lastnost uporabe Bash skriptov je, da je mogoče tovrstne datoteke preprosto prenašati med »sorodnimi« okolji.

Poglejmo še nekoliko pobližje, kaj dela lupina. Po prijavi lupina najprej prebere »konfiguracijske« oziroma inicializacijske parametre. S pomočjo vnosa lahko poženemo ukaze, ki so del ukazne lupine, ali poženemo »zunanje« programe. To pomeni, da po vsaki potrditvi vnosa lupina izvede kup opravil. Znotraj ukazne lupine lahko poženemo nove lupine, ki se ugnezdijo. Pri zapiranju lupin poteka zapiranje v obratnem vrstnem redu – zadnja se zapre lupina, ki je bila aktivirana prva. Ukazna lupina torej ni le okolje za »tipkanje« ukazov, prej zmogljiv »prevajalnik« in »komunikacijsko« vozlišče, ki ga uporabljajo tudi drugi. Problem oziroma odkrita ranljivost Basha je videti v precej drugačni luči.

Brez dvoma boste hitro lahko našli tudi skeptika, ki bo omenjeno ranljivost, predvsem pa stopnjo označil kot pretirano. Eden od možnih argumentov za takšno razmišljanje je lahko, da strežnik ni dostopen z interneta, ali recimo dejstvo, da uporabnik nima dostopa do sistema oziroma ukazne vrstice. To lahko do določene mere res zmanjšuje tveganje, dejstvo pa je, da Basha ne uporabljamo le prek ukazne vrstice, ampak njegovo funkcionalnost posredno uporabljamo tudi drugje. Kot najverjetnejša tveganja za zlorabo se opozarja na uporabo skript CGI (Common Gateway Interface) oziroma vmesnika CGI, ki je vmesnik s spletnim strežnikom. Potencialno problematičen je tudi dostop prek SSH, saj omogoča izkoriščanje ranljivosti Basha. Nevarna je lahko tudi uporaba odjemalcev DHCP v povezavi z Bashem, saj lahko strežnik DHCP zlorabi sistem brez vednosti odjemalca.

V času odkritja ranljivosti Heartbleed je bilo ocenjeno, da je nevarnosti izpostavljeno več sto milijonov računalnikov po svetu. V primeru Shellshocka pa je poleg velikega števila naprav problematičen tudi tip. Ogrožen je širok spekter naprav – vse od kabelskih modemov, usmerjevalnikov in drugih tipov naprav, na katerih teče Linux, pa vse do strežnikov na internetu, na katerih gostimo različne spletne strani. Dejstvo je, da za vse naprave popravki ne bodo na voljo (na primer zaradi starosti naprave) ali pa določeni uporabniki sploh ne vedo, da je zaradi uporabe Basha njihova naprava sploh izpostavljena. Brez dvoma lahko tovrstna ranljivost pomeni hud udarec (ali zamik) za prihajajoči internet stvari.

PREDVSEM BREZ PANIKE

Previdnost in kanček paranoje nikoli ne škodita, vsekakor pa se je treba izogniti paniki. Pred morebitno slabo voljo je treba ugotoviti, ali je naš sistem izpostavljen ranljivosti. Najprej slaba novica: če popravka Bash v zadnjem času (od konca septembra letos) niste naredili skupaj z drugimi posodobitvami in uporabljate Bash kot privzeto lupino, skoraj zagotovo je. Lahko pa to tudi preprosto poskusimo. Bashdoor oziroma Shellshock opisuje skupino ranljivosti (CVE-2014-6271, CVE-2014-6277, CVE-2014-6278, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187), ki jih lahko preprosto ugotovimo iz ukazne vrstice. Na spletnih straneh je precej napotkov, kako lahko preizkusimo, ali ima sistem navedene ranljivosti – omenimo le test na spletni strani https://shellshocker.net/, ki ga aktiviramo s pomočjo ukaznega niza curl https://shellshocker.net/shellshock_test.sh | bash. Morebitne ranljivosti lahko preizkusimo tudi s pomočjo nizov, ki jih vnesemo v ukazno vrstico. Ker so testi na različnih straneh praktično enaki, jih objavljamo brez sprememb.

Test prve ranljivosti izvedemo tako, da v ukazno vrstico vnesemo niz: env x='() { :;}; echo vulnerable' bash -c "echo this is a test". V primeru izpisa »vulnerable« je treba izvesti posodobitev Basha. Morebitno drugo ranljivost lahko poskusimo s pomočjo niza env X='() { (shellshocker.net)=>\' bash -c "echo date"; cat echo; rm ./echo. V primeru morebitnega izpisa datuma oziroma napake ranljivost obstaja, tudi če ste Bash že nadgradili s starejšim popravkom. Če v primeru vnosa ukaznega niza
env X=' () { }; echo hello' bash -c 'date' dobimo izpis hello, dobimo potrditev tretjega primera ranljivosti. Preverjanje četrte ranljivosti izvedemo s pomočjo daljšega niza bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' ||
echo "CVE-2014-7186 vulnerable, redir_stack", ki nam v primeru »luknje« izpiše besedilo CVE-2014-7187 vulnerable, word_lineno. Razmeroma preprost je tudi niz za test šeste ranljivosti – shellshocker='() { echo You are vulnerable; }' bash -c shellshocker, ki nam v primeru ranljivosti izpiše besedilo You are vulnerable. Končajmo z v času pisanja besedila zadnjim znanim primerom ranljivosti – če je sistem izpostavljen, nam ukazni niz bash -c "f() { x() { _;}; x() { _;} <<a; }" 2>/dev/null || echo vulnerable izpiše besedo vulnerable.

POTRDILI SMO RANLJIVOST, KAKO NAPREJ

Po potrjeni diagnozi ranljivosti moramo ugotoviti, katere možnosti imamo na voljo za rešitev. V primeru večine najbolj priljubljenih distribucij lahko to storimo dokaj preprosto s pomočjo paketov. V primeru distribucije Ubuntu (http://www.ubuntu.com/) to storimo s pomočjo ukaznih nizov sudo apt-get update in sudo apt-get install bash --only-upgrade bash. Pogoj, da lahko uporabimo tovrsten postopek, je, da je nameščen sistem še vedno podprt. V nasprotnem primeru je najprej treba posodobiti/nadgraditi starejšo različico (sudo do-release-upgrade), šele nato lahko izvedemo posodobitev.
Če imamo na določeni napravi nameščeno starejšo distribucijo, ki je iz takšnega ali drugačnega razloga ne moremo ali ne želimo nadgraditi, imamo drugo možnost. Žal takšna rešitev zahteva več dela in v praksi ni vedno izvedljiva, saj bomo novo različico Bash prevedli sami. Nameščene različice ne bomo nadgrajevali v smislu različice, ampak bomo obstoječo različico le nadgradili s potrebnimi varnostnimi popravki.

Trenutno različico Bash ugotovimo tako, da v ukazni vrstici vnesemo niz bash -–version, ki nam izpiše trenutno različico (izpis recimo 4.0.1 pomeni različico 4.0). Drug zelo pomemben korak je arhiviranje obstoječe različice (zakaj, bo jasno v nadaljevanju). Različico Bash poiščemo (na primer which, find / | grep …), običajno pa je v imeniku /bin/bash. Kopijo lahko naredimo kot korenski uporabnik ali s pomočjo ukaza sudo cp /bin/bash /bin/bash.old (oziroma bak, original …). Sledi vzpostavitev okolja za posodobitev. V imeniku /usr/local/src naredimo nov imenik bashfix (mkdir /usr/local/src/bashfix) in se postavimo v ta imenik (s pomočjo ukaza cd). Če v sistemu nimamo ustreznega okolja, namestimo še pakete, potrebne za prevajanje (patch, bayacc, bison,autoconf …).

V pripravljeni imenik bomo prenesli »izvorno kodo« Basha s spletne strani https://ftp.gnu.org/pub/gnu/bash/. Prenos lahko izvedemo na različne načine (na primer s pomočjo ukaza wget, spletnega brskalnika …), paziti moramo le, da prenesemo pravo različico (ki smo jo ugotovili s pomočjo bash –version). Arhivsko datoteko odpremo v imeniku bashfix s pomočjo ukaznega niza tar -xzvf bash-stevika_razlicice.tar.gz. Ko je nameščena še ta datoteka, se prestavimo v imenik bash-stevilka_razlicice.

V imeniku imamo kodo osnovne različice brez popravkov. Popravke lahko naložimo s pomočjo različnih skript, lahko pa popravke (patch) kode naredimo ročno. Pazljivi moramo biti, da naredimo vse popravke! Patch lahko izvedemo na različne načine (na primer curl http://pot_do_patch_datoteke/ime_patch_datoteke | pach -p0). Ker je lahko število popravkov veliko (v primeru aktualne različice 4.3 jih je bilo do 5. 10. skupaj 30), vam priporočamo, da poiščete skripto, ki bo postopek naredila namesto vas. Ko je koda s pomočjo popravkov (patch) ustrezno popravljena, jo pripravimo s pomočjo ukaza ./configure in prevedemo s pomočjo ukaza make.
Če smo imeli nameščene vse potrebne komponente in nismo naredili kakšne napake, dobimo varnostno posodobljeno lupino Bash obstoječe različice. Preverimo lahko še, ali ima nova različica (binarna datoteka) enake uporabniške pravice kot stara različica Bash, in jo prekopiramo/prepišemo v imenik, kjer je prvotna različica. To storimo kot korenski (root) uporabnik oziroma s pomočjo ukaza sudo cp -f bash /bin/bash. Na tem mestu še opozorilo, saj gre za praktično izkušnjo avtorja. Zaradi večkratnega posodabljanja na več strežnikih je prišlo zaradi kančka nepazljivosti do napake. Posledica zamenjave obstoječe različice Bash z novo se je pokazala v onemogočeni prijavi v sistem (tako lokalno kot prek SSH). Edina rešitev za to težavo je bila ponovni zagon sistema z drugega medija (USB-ključek oziroma CD), s pomočjo katerega se je začasno naložil tako imenovani »live« sistem v pomnilnik (v našem primeru Ubuntu), brez namestitve na trdi disk. S pomočjo tovrstnega zagona je bil omogočen priklop trdega diska oziroma zamenjava nepravilne različice Bash s kopijo prvotne. Po povrnitvi v prvotno stanje smo ponovili celoten postopek ter prevedli in namestili novo različico.

Namestitev pokrpane različice še ne pomeni varnega spanca. Po namestitvi popravkov je treba preveriti, ali je nova različica varna pred ranljivostmi oziroma ali se morda ni odkrila še kakšna nova. V tem primeru je treba izvesti in ponovno ponoviti celoten postopek (preverjanje, modifikacija, prevajanje, namestitev, preverjanje).

Moj mikro, november – december 2014 | Marko Koblar