LS17 – üks jube tudengiprojekt “väikse” tagauksega

Siin postituses kirjeldan eelkõige ainult omaenda tegevust, kuid suures plaanis oli see vaid tilk meres, iga teine võistkonna liige tegi oma süsteemide kaitsmisel vähemalt sama palju või rohkem.

Veebirakenduse kaitsmisest pole suurt kasu, kui näiteks võrk ei tööta või serverid ei käivitu. Pealegi oli see vaid üks osa tehnilisest süsteemist, aga lisaks tehnilisele käis kibe töö ka muus osas, mis käis juba üle meie peade. Juriidiline osa, meedia, koordineerimine jne.

See postitus on teine osa järjejutust, mis võtab kokku Andrise ja Peetri poolt Locked Shields 2017 küberõppusel kogetu.
Loe ka:

Ettevalmistus

Esialgu teadsime nendest rakendustest ainult nii palju, kui üldskeemi pealt oli näha. Osaliselt võis aimata, et millega tegu, osaliselt mitte nii väga, nii et vähemalt mina midagi väga ette planeerida ei osanud.

Tutvumispäevadel, nädal enne õppuse algust, pääsesime ligi juba kõikidele süsteemidele, va. üks puuduv veebirakendus, mille kohta oli teada ainult selle üldkirjeldus. Kuna see veebirakendus oli üks minu ülesannetest, siis jäi sellevõrra rohkem aega vaadata üle muid oma vastutusvaldkonna asju. Koos kolleeg Peetriga majandasime erinevaid veebirakendusi ning saime selleks vajalikku tuletoetust Linuxi adminnidelt.

Esialgu tundus kõik üsna keeruline, tegelesin e-posti serveriga, kuhu olid paigaldatud ka veebipõhised mailikliendid ning rakenduste paljususe tõttu paistis auke igalt poolt. Minu suurimaks lemmikuks oli näiliselt juhuslike “vigade” (muutuja nime muutev trükiviga siin, konfiguratsiooniviga seal jne.) tõttu tekkinud olukord, kus veebi kaudu ligipääsetavasse faili logiti kõike, sh. kõikide kasutajate paroole, mida normaaloludes kuidagi juhtuda ei tohiks.

Teise päeva lõpuks tundus asi juba täiesti kontrolli all olevat. Toimetasin serveris rahulikult ringi, otsisin vigu, parandasin neid ning tegin koopiaid (vahetult enne võistlust tehti kõikidele süsteemidele initsialiseerimine ja parandused oleksid kaotsi läinud), kui monitooringu poolelt tuli äkki teade, et sellessamas masinas istub mingi beacon. Läksin ja vaatasin, tõepoolest olid monitooringus näha kummalised payloadid C&C serverisse.

Vasak silm hakkas tõmblema.

Päev 0

Esimene õppusepäev oli samuti harjutuspäev. Nüüd olid kõik süsteemid lõplikul kujul üleval ja saime nendega kuni õhtuni toimetada, mil need seekord juba viimast korda initsialiseeriti. See muidugi tähendas, et ka minule määratud üllatusrakendus oli masinas olemas.

Esialgu paistis see olevat tavaline PHP+MySQL veebirakendus, kuid ühes kaustas asusid veel mingid shelli skriptid ja teises asus midagi, mis paistis nagu Pythoni deemon. Juba põgusa pealevaatamise alusel võis hinnata, et rakendus oli kõikvõimalikke auke täis. Mis polnud ka ime, arvestades, et rakendus oligi kirjutatud spetsiaalselt selle võistluse jaoks.

Esimese asjana võtsin ette PHP koodis SQL-injectionid. Neid oli muidugi igat sorti, osaliselt olid koodis SQL argumendid töödeldud addslashes funktsiooniga SQL päringu stringi sees, osaliselt pandi $_REQUEST muutujast tulevad väärtused lausesse otse (injection!), osaliselt kasutati custom töötlemisfunktsioone, mis kasutasid muidu küll addslashes’it, aga tagastasid tulemusest vaid fikseeritud pikkusega osa (peidetud injection!), mis omakorda oleks võimaldanud stringe lõpetada tagurpidi kaldkriipsuga.

Joonis 1.  Kui $user_id oleks string, tuleks sealt eemaldada kõik, mis võimaldab kavandatud SQL lausest välja hiilida, täisarvu puhul oleks õigem teha kohe intval($user_id). addslashes() on turvalisema mysqli_real_escape_string() asemel kasutusel kuna maandas enamuse riskist, oli lihtsam lisada ja võimaldas seetõttu suurema hulga koodi üle käia. Päris-elus tuleks võtta kasutusele PDO ja prepared statement. Selles kiiruga tehtud paranduses on muideks üks kala sisse jäänud  😉

Parandasin seejärel kõikvõimalikke muid koodi ja konfiguratsioonivigu. Küll oli vales kaustas lubatud valet tüüpi faile käivitada kui PHP’d (kirjutasin uued .htaccess reeglid kõikide kaustade jaoks), küll olid pildifailid PHP koodi täis (kirjutasin hex editoriga nendesse uue koodi), kõikvõimalikud tmp ja puhvri kaustad asusid web root kaustas ning olid muidugi ka veebist loetavad. Siis veel mitmel eri viisil koodi käimatõmbamine valest failist (ehk include $_REQUEST[‘page’];), veebist loetavad konfiguratsioonifailid (MySQL konto andmed!), kasutaja sessiooni andmete muutmine cookie kaudu jne.

Sinna juurde veel vead Apache enda konfiguratsioonis (täiendav rivi faililaiendeid, mida käivitati PHP koodina, lahtine cgi-bin, server-status jmt), eelinstalleeritud cron, kummalised PHP seaded (aegunud sessioonide GC oli välja lülitatud) jmt. Päeva lõpuks tundsin juba end päris hästi, paistis, et kõik on kontrolli all.

Joonis 2. Kui oled lasknud oma serveri üle võtta, tasub kahelda kõiges. Ükshaaval selliste pisimuudatuste otsimine on võimatu – turvalisem on kõik-kõik-kõik asendada originaalide või oma koostatuga. Ning jälgida, et süsteem ikka tõesti võtab konfifaile sealt, kust sina arvad.
Päev 1.

Algas esimene tegelik õppusepäev, kõik süsteemid olid taastatud jälle algseisu ning meile anti pool tundi peale mängude käivitamist, et oma parandused süsteemis ära teha. Kuna automaatsete parandustega tekkis mingi jama, lükkasin oma veebirakenduse muudatused üles kiirelt käsitsi. Kontrollisin, kõik töötas, läksin võtsin kohvi.

36 minutit ja 6 sekundit peale mängude algust lendas peale esimene rünnak, mis kohe ka õnnestus. Rakenduse veebilehelt vahtis vastu “Peace Brigade is here” sõnum, maatriksi rohelised kukkuvad sümbolid taustal. Suu kukkus lahti, mul oli kõik ju kontrolli all?

Tuli välja, et muidugi ei olnud asjad kontrolli all. Olin ära unustanud templiidifailid põhjalikult üle käia. Kuigi osa html koodi escape’imist tegin ära juba PHP koodi poolel, siis põhitöö käis templiitides. Midagi olin nagu teinud, aga ilmselt mitte piisavalt. Ahvikiirusel käisin siis kõik templiidifailid läbi ja toppisin escape filtreid täis ning kirjutasin baasis muudetud kirjed üle, et defacementi eemaldada.

Päris korda kohe kõike ei saanud, veidi läks veel neidsamu defacemente läbi ning lisaks õnnestus mul osad templiidid nii ära rikkuda, et osa funktsionaalsusest enam ei töötanud, kuna templiiti ei saanud laadida. Parandasin süntaksivead ükshaaval ära ning tundus, et kõik on jälle kontrolli all.

Kell 13:00 hakkas teine faas ning sellega koos hakkasid laekuma SQL-injectioni katsed. Nendega paistis siiski kõik korras ning kasutajad nimega 1=1 sisse logida ei saanud.

Joonis 3. Syslog ei pruugi olla küll kõige mõistlikum koht… aga hädaga ajab kiirema asja ära. Allpool loginäide lihtsamat sorti SQLi’st:
Apr 26 10:09:45 scheduler[9524]: url=/admin/login.php message=Login failed for "or/**/1=1# session=[]
Apr 26 10:09:45 scheduler[9524]: url=/admin/login.php error=Wrong username or bad password session=[]

Veel 10 minutit hiljem hakkasid tuvastamata põhjustel toimuma anomaaliad. Kõigepealt tekkisid vead rakenduse näitamisel, ilmnes et kettaruum on otsa saanud. Kettaruumi oli ära kulutanud /tmp kaustas asuv hiigelsuur fail. Vaatasin seda less’iga ja nägin vaid suurt kogust nullbaite.

Siis juba ilmnesid probleemid baasiga. Või õigemini selle puudumisega, keegi oli nimelt kell 13:41 andmebaasi ära kustutanud. Lisaks ilmselgele probleemile – veebikasutajal oli õigus kutsuda välja DROP DATABASE, mis sai käigu pealt ka parandatud, oli ikkagi küsimus, et kuidas?

Taastasime baasi backupist.

Mõne aja pärast hakkas veebirakendus näitama out of memory veateateid. Tuli välja, et baas oli jälle läinud. Seekord küll mitte täielikult (polnud enam kustutamise õiguseid), vaid tabelid olid tühjaks tehtud, va. üks mis oli täis gigabaitide jagu x tähti (selleks andis võimaluse tõsiasi, et viimne kui üks tekstiväli andmebaasis oli TEXT tüüpi). PHP rakendus proovis kõiki neid ridu ühte massiivi sisse lugeda ja sellest ka veateade.

Taastasime baasi backupist.

Nüüd läks asi päris hulluks. Otsustasin teada saada, et kuidas see kustutamine juhtub ning hakkasin kõikvõimalikku logimist peale keerama. Viga parandada ei proovinud, kuna ei teadnud, et kust otsida. Selle asemel lootsin saada uutest rünnakutest piisavalt palju infot, et auk selle kasutamise järgi üles leida.

Joonis 6. Tavapäraselt jookseb veebiserveri logisse päring – ja GET-päringu puhul ka parameetrid. POST-päringu body’s olevad parameetrid, aga ka küpsised jms, jäävad peitu. Nende logimiseks saab kasutada nt Mod Security’t, aga kui on kiire, tuleb leiutada. Igas rakenduses on mingi konfifail, mis laetakse igal päringul – see on ideaalne koht oma logimislahendusele… ja pilt on lõigatud ära realt, kust algas kaitsemeede.

Keegi kustutas baasi ära, meie lasime selle kohe tagasi ja nii mitmeid kordi. Lõpuks hakkas tekkima failidesse pahaseid sõnumeid, et tegeleme cheatimisega. Mõtlesin, et võiks kuidagi vastata, aga ei viitsinud. Sest lõpuks oli eesmärk käes, sain piisavalt infot, et mis toimub. Paraku kaasnes sellega ka 250 miinuspunkti, sest pidev taastamine kohtunikele ei meeldinud.

See keegi kasutas rakendust ennast webshellina. Saatis sinna kodeeritud pakette, mis tõmmati mingil viisil PHP koodina käima. Päris hästi ei saanud aru, et kuidas see mehhanism töötab, aga paha payload oli kätte saadud ning panin sisenemisaugu kinni, nii et seda rohkem käivitada ei saaks.

Joonis 4. Veebirakendus endiselt töötab

Ma ei hakka siinkohal avaldama, et kuidas webshell täpselt töötas, kuna hammustasin selle lahti alles peale võistlust. Võistluse ajal sain vaid aru, et payload on base64 kujul mingisugune läbu, olid omad kahtlused selle sisu formaadi kohta, aga päris kindel ei olnud. See jõudis rakenduseni ja seal siis mingi asi võttis payloadi lahti ja pani käima.

“Aga miks te lihtsalt päringu parameetreid ei filtreerinud?” Kui pahalane on sinu koodis sees, siis võib ta mõelda välja lõputult lahendusi, kuidas see kood saab oma peremehega suhelda. Oleme kavalad ja teeme intval($some_id) – aga võibolla tilguvad käsud sisse selle sama ID järjestikuste väärtustena? Ainus lahendus on tagauks üles leida, ideaalmaailmas tehes totaalse code-review… piiratud ressursi puhul tuleb logida ja leppida sellega, et mõned ründed läbi pääsevad.

Vaikselt hakkas ka paranoia tekkima. Iga nurga tagant paistsid punaste kõrvad:

Mul on siin mingid kummalised OPTIONS päringud. JA NEED TULEVAD LOCALHOSTIST!
Need on monitooringupäringud
Aa okei, hea küll

Päeva lõpus prooviti veel üht auku, mis oli pool-lahti. St. et saadi käima tõmmata PHP funktsioone, kuid selleks funktsiooniks, mida käima prooviti saada, oli system (kusjuures jälle läbi templiidisüsteemi, aga teise augu kaudu), mis oli Linuxi adminni poolt just 5 minutit tagasi ära keelatud. Seega ei läinud see funktsioon käima ning suutsin vastava augu ka kiirelt kinni panna, et teisi funktsioone proovida ei saaks.

Kui kell viis mängu võrk kinni pandi oli kergendus suur, aga tunne oli pigem positiivne, kõik paistis olevat kontrolli all. Õhtul võtsin koodi kodus ette ja käisin templiidid veel korralikult üle, et kõik oleks korralikult escape’itud.

Päev 2.

Esimese asjana uuendasin koodifailid ära, et oleks kõik kindel. Paraku hakkasid üsna pea samad jamad peale nagu eelminegi päev. Andmebaasi ilmus kummaline rida uue kasutajaga, kellel olid märgitud rakenduse adminni õigused. Toimusid mingid anomaaliad. Tundus, et midagi oli selle templiidisüsteemiga endiselt väga valesti. Käisin järgmiseks üle koodi poole, kus templiiti välja kutsuti ning eemaldasin kõik templiidile edastatavad muutujad, mida polnud hädasti vaja. See paistis aitavat, anomaaliad kadusid. Tagantärgi selgus, et olin õhtuste parandustega vana augu uuesti lahti teinud.

Üsna pea tekkisid katsed kasutaja õiguste eskaleerimiseks, kus küpsise abil üritati sättida endale selliseid sessiooni andmeid, mida tegelikult ei olnud. Seda võimaldav viga oli juba ammu parandatud ja seega üritus läbi ei läinud.

Küll aga olid endiselt alles tegelikult juba eelmisel päeval välja tulnud probleemid õigustega – kasutajad said teha rohkem, kui neil oli lubatud. Meetodite autoriseerimiskontrollid olid nõrgad (adminnile ette nähtud tegevusi sai teha ka tavakasutaja) või puudusid üldse (igaüks kes oskas õige päringu koostada sai tegevust läbi viia). Ning seda muidugi kasutati ka ära. Õnneks sain nende aukude puhul kiirelt jaole ja muutsin käsitsi muudetud andmed tagasi, seekord me andmebaasi taastamist ei teinud.

Mingi hetk läks juba igavaks, kõik teadaolevad augud olid kinni, mis andis võimaluse minna üle ofensiivsemale taktikale. Kuna mul oli logimine päris hästi paigas, oli reaalajas ülevaade, et mis parasjagu rakenduses toimub ja kes seal toimetab, tuvastasin kahtlase liikluse kohe selle tekkimisel.

Kohe kui ründaja midagi teha üritas, tegin oma liigutused vastu. Ründaja registreeris uue konto. Mina kustutasin selle konto baasist ära ja tegin PHP sessiooni kausta tühjaks. Ründaja tegi uue konto. Mina kustutasin ära. Ja nii väga palju kordi. Vahepeal lasin ka veidi toimetada, vaatasin, et millega tegeleb, aga esimese POST päringu peale lendas konto jälle minema. See ei olnud mingil viisil skriptitud, tegin kõike käsitsi.

Ühel hetkel aga hakkas üks “päris” kasutaja kurtma, et tal ei õnnestu uude kontosse sisse logida, mille tagajärjel pidin oma hoogu maha tõmbama. Hiljem tuli välja, et kuna punastel konto loomine minu vastutegevuse tõttu (millest nad ei teadnud) ei õnnestunud, palusid nad seda proovida ühel “päris” kasutajal ning kuna see sattus ajaliselt ja välimuselt kokku punaste tegevusega, arvasin, et ka see “päris” kasutaja on punane ja kustutasin ka tema kontod ära. Üldine arvamus punaste poolel oli, et tegeleme cheatimisega, taastame cronist baasi iga hetke tagant ning peaksime saama miinuspunkte. See siiski ei olnud nii ja õnneks miinuspunkte ka ei saanud.

Punaste kahjuks ja minu õnneks oli tava-trafficu genereerimine korraldajate poolt üsna kehval järjel või puudus üldse, mis andis kogu punaste tegevuse mulle selleks hetkeks nagu peo peal ette. Nemad arvasid, et tegutsevad suitsukatte all, mina aga vaatasin seda tegevust sisuliselt läbi suurendusklaasi.

Joonis 5. Tüüpiline monitooringuvaade veebirakenduse staatusest võistluse ajal

Päris lõpus keskendusid põhirünnakud juba muude süsteemide vastu, veebi poolel midagi põrutavat ei toimunud. Üritati vahelduva eduga mingeid lihtsamaid SQL-injectioneid stiilis id=sleep(1337) ning ka skriptifailide üleslaadimist, aga kui midagi osaliselt kehvade õiguste kontrolli tõttu läkski läbi, sain sellise augu momentaalselt elimineeritud, nii et ründaja ei saanud ilmselt arugi, kui tal miski õnnestus.

Tulemus

Veebiosa lõpptulemus oli väga hea, kuna punased suutsid püstitatud eesmärkidest meie vastu saavutada vaid üksikud. Peetri asjadest ei saadud vist üldse läbi ja minul olid need mõned custom rakenduse augud, millest läbi saadi. Väga suur osa selles tulemuses oli ka Linuxi adminnidel, tänu kellele sai leitud mitmeid konfiguratsiooniauke ning kes aitasid rakendusi kogu selle aja püsti hoida. Oli väga hea tiimitöö ning tulemus oli ka näha, sest meie võistkond sai kokkuvõttes teise koha, kaotades üldvõitjale väga napilt.

Ühtepidi oli muidugi kahju, et võistlus läbi sai, aga nii intensiivset koormust taluda ei oleks enam väga kaua suutnud. Kokkuvõttes võib öelda, et sellised õppused on väga vajalikud, kuna reaalse ründesituatsioonita võib ju teoretiseerida, et kuidas end kaitsta, aga kui käsi tegelikult mustaks ei tee, siis see jääbki ainult teooriaks. Õppisin nädalaga rohkem, kui muidu aastaga ja see vist oligi kogu asja eesmärk. Mission accomplished.

Järjejutu kõik osad:

Autor: Andris Reinman

Andris on üks Zone.ee infosüsteemi arhitekte. Lisaks arendab ta Zone MTA nimelist SMTP serverit (https://github.com/zone-eu/zone-mta), Node.js e-posti moodulit Nodemailer (https://nodemailer.com) ja postiloendite haldamise tarkvara Mailtrain (http://mailtrain.org/)