Contribee - move fast and break things
2022 m. vasario 24 d. rusija įsiveržė į Ukrainą. Viso pasaulio žmonės aukojo pinigus įvairioms organizacijoms padedančioms ukrainiečiams. Tuo tarpu kontraversiškas Patreon sprendimas suspenduoti ne pelno siekiančią organizaciją “Come Back Alive” paskatino nemažai skaitmeninio tūrinio kūrėjų, bent jau Lietuvoje, persikelti į alternatyvią platformą Contribee. Aš, kaip ir kiti prenumeratoriai, užsiregistravau šiame puslapyje. Tačiau greitas platformos testas parodė, kad ji gali turėti saugumo spragų.
TL; DR;
Pranešiau apie aptiktą SQL injekciją, Reflected ir Stored XSS, nepakankamą duomenų validaciją, autorizacijos spragas ir informacijos atskleidimą per Contribee API. Pasinaudojus šiais pažeidžiamumais buvo galima prieiti prie visos duomenų bazės informacijos ir potencialiai ją keisti, atlikti veiksmus kitų vartotojų vardu, nusipirkti bet kurio kūrėjo ir lygio prenumeratą už minimalią 0.50 Eur kainą, trinti svetimus komentarus ir gauti tokią privačią informaciją kaip kūrėjo pajamos arba komentatoriaus el. pašto adresas.
1. Reflected XSS paieškoje
Suvedęs paieškos laukelyje stebuklingus žodžius pamačiau pranešimo langą:
Kuo tai pavojinga ir kaip pasinaudoti tokiu pažeidžiamumu jau rašiau praeitame straipsnyje. Šį kartą tik pridėsiu, kad XSS leidžia atlikti veiksmus prisijungusio vartotojo vardu. Tam nėra būtina vogti slapukus. Pasinaudojus automobilio analogija, jei kažkas svetimas gali perimti važiuojančios mašinos vairą - tai jau pakankamai blogai, net jei jis negali pavogti jos raktų.
2. SQL injekcija paieškoje
Tame pačiame paieškos funkcionalume kur pastebėjau XSS buvo pasislėpusi ir SQL injekcija. Vienas langelis - dvi spragos. Šis pažeidžiamumas pirma prasprūdo pro akis, nors faktiškai tai tapo rimčiausia atrasta Contribee spraga, galėjusi leisti tiesiogiai prieiti prie visos duomenų bazės informacijos ir potencialiai ją keisti. Kaip įrodymą įvedus į paiešką a') AND 7143=(SELECT 7143 FROM PG_SLEEP(5))--
serveris vykdė užklausą penkiomis sekundėmis ilgiau nei įprastai.
3. Prenumeratos kainos apėjimas
Contribee platformoje kiekvienas kūrėjo nustatytas prenumeratos lygis turi skirtingą kainą bei privalumus.
Paspaudus prenumeruoti leidžiama pasirinkti prenumeratos kainą. Jei pasirinkta kaina yra mažesnė nei nustatyta lygiui, Contribee rodo klaidos pranešimą.
Tačiau šis apribojimas buvo tik naršyklės pusėje. Pamodifikavus į serverį siunčiamoje užklausoje priceRec
reikšmę serveris sėkmingai apdorodavo ją. Reikia paminėti, kad 0.50 Eur yra minimalus Stripe, kurį Contribee naudoja pinigų surinkimui, palaikomas dydis. Kitaip, ko gero, galima būtų sumokėti ir vieną centą, arba nulį.
Šis pažeidžiamumas leido sėkmingai “atrakinti” bet kurią prenumeratą už minimalią sumą.
Po pranešimo Contribee ištaisė šią klaidą. Tačiau atradau kitą būdą pasiekti tą patį.
Prenumeratos užsakymo nuoroda atrodo kaip https://contribee.com/tmarciu/order/2647 arba https://contribee.com/adrijus-j/order/9. Pakeitus skaičiuką nuoroda vis tiek sėkmingai veikė. Taigi kūrėjo ID nebuvo niekaip susietas su unikaliu prenumeratos numeriu. Suvedus naršyklėje dirbtinę nuorodą kaip https://contribee.com/tmarciu/order/9 (kombinacija tmarciu
ir 9
) ir apmokėjus 1 Eur prenumeratą pinigai keliavo adrijus-j
, bet “atrakindavo” brangesnę tmarciu
prenumeratą.
Ar kas nors iš Contribee tūrinio kūrėjų turi prenumeratą už 0.50 Eur? Tai lengva “nuscrapinti”. Patikrinus visus prenumeratų numeriukus nuo 0 iki 9999 gauname:
Mano nuostabai prenumeratų su minimalia kaina 0.50 neradau, tačiau gavau sąrašą prenumeratų su minimalia kaina 0 Eur. Deja, Stripe nepraleidžia tokio mokėjimo:
Bet tai ne bėda, nes prenumeratorius gali mokėti daugiau nei nustatyta minimali kaina.
Dėl PVM ir kitų mokesčių, galutinė kaina yra šiek tiek didesnė - 0.84 Eur. Ar galima pigiau? Kiekvienas gali tapti Contribee tūrinio kūrėju ir sukurti savo prenumeratą. Taigi buvo galima mokėti 0.50 Eur sau ir išlaidos būtų tik 0.34 Eur.
4. Stored XSS komentaruose
Vartotojo vardas buvo interpretuojamas kaip HTML, kas leido “apkrėsti” komentarus.
5. Svetimų komentarų trynimas
Contribee leidžia trinti savo paties komentarus. Užvedus pelę ant tokio komentaro atsiranda šiukšliadėžės paveiksliukas. Virš svetimų komentarų jis neatsiranda.
Kadangi kiekvieno komentaro numeris matosi HTML kode, galima tiesiai nusiųsti užklausą į serverį su svetimo komentaro numeriu. Ar vartotojas turi teisę ištrinti komentarą buvo tikrinama tik naršyklės pusėje.
6. Kūrėjo pajamų atskleidimas
Contribee tūrinio kūrėjai gali nustatyti ar jų pajamas per mėnesį rodys viešai arba slėps. Pvz. kur kūrėjas viešina šią informaciją:
HTTP užklausos lygmenyje tai atrodo taip:
/api/get/basic-information/2699
{"subscribers":1033,"mrr":"4,959 \u20ac","onetime-tips":"15 \u20ac","goals":[],"currency":"EUR"}
O taip atrodo kai kūrėjas nusprendė nerodyti šios informacijos:
Tačiau HTTP užklausos lygmenyje pajamas vis tiek rodė:
api/get/basic-information/2851
{"subscribers":71,"mrr":"■■■ \u20ac","onetime-tips":"0 \u20ac","goals":[],"currency":"EUR"}
7. Komentatoriaus el. pašto adreso atskleidimas
Užkraunant komentarus Contribee siunčiasi JSON duomenis su daug perteklinės informacijos. Tarp jos buvo ir vartoto el. pašto adresas kuriuo jis registravosi platformoje.
Chronologija:
2022.03.08 - Išsiųstas el. laiškas su ataskaita (Prenumeratos kainos apėjimas, Stored ir Reflected XSS, Svetimų komentarų trynimas) į Contribee support ir NKSC.
2022.03.09 - NKSC pranešė, kad susisiekė su Contribee telefonu.
2022.03.13 - Išsiųstas el. laiškas apie SQL injekciją.
2022.03.14 - Contribee padėkojo ir pranešė, kad taiso klaidas.
2022.04.01 - Išsiųstas el. laiškas apie privataus el. adreso ir kūrėjo pajamų atskleidimą.
2022.05.25 - Išsiųstas el. laiškas apie dar vieną prenumeratos kainos apėjimą.
2022.06.06 - Pažeidžiamumai pašalinti. Informacijos paviešinimo data.