Hoppa yfir í efnið

Vefkóðainnskot

Vefþjónninn sendir frá sér HTML-kóða sem vafrinn síðan túlkar. Lendi notandastýrt gildi í svarinu sem virkt HTML eða JavaScript, getur svo farið að vafrinn keyri óvæntan kóða innan uppruna vefsins. Þetta fyrirbæri nefnist vefkóðainnskot (e. cross-site scripting, XSS).

XSS-veikleikinn snýst um margt fleira en það eitt að fá alert(1) til að skjóta upp kollinum. Kóðinn keyrir með nákvæmlega sömu vafraheimildum og síðan sjálf; honum er því kleift að lesa sýnileg gögn af sama uppruna, senda beiðnir undir gervilotu notandans og breyta viðmótinu að vild. Hver hin raunverulega geta er ræðst þó ætíð af samhenginu, eiginleikum viðkomandi koku, sama-upprunareglunni og svo auðvitað öryggisstefnu efnis (CSP).

Verkefnið Leitarljós varpar leitarorði ókóðuðu beint aftur í HTML-kóðann. Við færum sönnur á keyrsluna með staðbundinni breytingu á DOM-trénu og sækjum því næst /flag innan sömu gervilotu. Fáninn birtist einvörðungu í þínum eigin vafra; þar kemur hvorki við sögu neinn stjórnandabot né ytri gagnasending.

Engin gagnasending út fyrir æfinguna

Prófaðu XSS aðeins í þessu verkefni eða eigin staðbundnu síðu. Ekki setja inn kóða á raunverulegum vefjum, ekki senda kökur eða fána á ytri móttakara og ekki búa til fölsuð innskráningarviðmót. Verkefnið er leyst með staðbundinni DOM-breytingu og sama-uppruna flaggleið.

Uppspretta, svelgur og samhengi

Gagnlegt líkan hefur þrjá hluta.

Inntaksuppspretta (e. source) nefnist sá staður sem hið ótrausta gildi á uppruna sinn hjá. Það gæti verið:

/search?q=ull

formreitur, JSON-svar, location.hash, localStorage eða skilaboð frá öðru vafrasamhengi.

Svelgur (e. sink) er svo sú aðgerð sem hlutast til um að setja gildið í túlkanlegt samhengi. Dæmi eru HTML-sniðmát á þjóninum, innerHTML, document.write() eða JavaScript-túlkun.

Úttakssamhengi (e. output context) segir til um hvaða þáttari (e. parser) fær textann til úrvinnslu hverju sinni:

HTML-texti
HTML-eigind
JavaScript-strengur
vefslóð

Sama inntakið tekur á sig ólíka mynd eftir því í hvaða samhengi það hafnar. Rannsókn á XSS-veikleikum hefst því jafnan á því að leiða í ljós hvar merkið okkar lendir nákvæmlega, fremur en að þreifa sig áfram með einhverja eina „alhliða“ nytjahleðslu (e. payload).

Endurvarpað XSS

Endurvarpað XSS (e. reflected XSS) raungerist þegar gildi úr yfirstandandi beiðni skýtur upp kollinum undireins í svarinu.

Beiðni:

GET /search?q=ull HTTP/1.1
Host: nordurmarkadur.example

Svar:

HTTP/1.1 200 OK
Content-Type: text/html

<h1>Leit</h1>
<p>Leitað að: ull</p>

Lími forritið gildið q beint inn, getur saklaust HTML-merki hneppt skipan svarsins í algerlega nýjan búning:

q=<strong>XSS_MARKER_42</strong>

og svarið orðið:

<p>Leitað að: <strong>XSS_MARKER_42</strong></p>

Feitletrað merki færir okkur sönnur á HTML-innskot, en segir enn sem komið er ekkert til um sjálfa JavaScript-keyrsluna. Hér er á ferðinni skynsamlegt milliskref; nú er ljóst að vafrinn tekur til við að túlka merki í hinu hefðbundna HTML-textasamhengi.

Geymt XSS

Geymt XSS (e. stored XSS) festir fyrst rætur í gagnagrunni, skrá eða annars konar geymslu, og skýtur svo upp kollinum í síðari beiðnum. Athugasemd gæti verið send með POST /comments, en keyrslan orðið þegar /article/17 er opnuð síðar.

notandi sendir gildi → þjónn geymir → annar skjár birtir → vafri túlkar

Í gagnaglímum skiptir höfuðmáli að átta sig á því hver það er sem opnar hið geymda gildi. Sum verkefni nota sérstakan stjórnandabot, en Leitarljós gerir það ekki. Við vinnum aðeins í eigin gervilotu.

Þegar geymt tilvik er rannsakað ber okkur að halda til haga bæði geymslubeiðninni sjálfri og þeirri beiðni sem annast birtinguna. Uppsprettan og svelgurinn eru þá ekki endilega á sömu slóð.

DOM-byggt XSS

DOM-byggt XSS (e. DOM-based XSS) á sér stað inni í sjálfum JavaScript-kóða vafrans. Hið hráa HTTP-svar kann að vera án sjálfs inntaksins, en JavaScript-kóðinn tekur sig þá til, les gildið og setur það í hættulegan DOM-svelg:

const message = location.hash.slice(1);
document.querySelector("#output").innerHTML = message;

Vefslóðarbrot er ekki sent þjóninum, en JavaScript sér það. Ef brotið inniheldur HTML túlkar innerHTML það.

Berum saman:

output.innerHTML = message;   // HTML-þáttari fær textann
output.textContent = message; // textinn birtist bókstaflega

textContent er sterk vísbending gegn HTML-innskoti á þeim svelg. JavaScript gæti samt notað sama gildi síðar í öðru samhengi, svo rekja þarf alla leiðina sem verkefnið sýnir.

Í DevTools skaltu bera View Source saman við Elements. Birtist inntakið einvörðungu í hinu lifandi DOM-tré í kjölfar JavaScript-keyrslu, má telja víst að DOM-byggð leið sé sökudólgurinn fremur en endurvarp frá þjóninum.

HTML-textasamhengi

Í HTML-texta lendir gildið milli merkja:

<p>Leitað að: INNTAK</p>

Með skaðlausu prófi, þar sem stuðst er við <strong> eða <em>, má glögglega sjá hvort nýtt merki verður í raun til. Til að staðfesta JavaScript-keyrslu í labbi má nota einingu sem framkvæmir staðbundna DOM-breytingu þegar villa verður:

<img src="/tilbuid-vantar" onerror="document.body.dataset.xss='confirmed'">

Ef kóðinn keyrir fær <body> eigindina:

<body data-xss="confirmed">

Slík aðgerð sendir engin gögn frá sér og opnar engan glugga. Þú getur staðfest breytinguna í Elements eða Console:

document.body.dataset.xss

sem á að skila "confirmed".

HTML-innskot er ekki alltaf XSS

Ef þú getur bætt við fyrirsögn en engin leið er að keyra JavaScript vegna samhengi eða vafrastefnu hefurðu staðfest HTML-innskoti, ekki endilega XSS. Skráðu nákvæmlega hvaða hegðun sást.

Eigindasamhengi

Gildi getur lent inni í HTML-eigind:

<input name="q" value="INNTAK">

Þá þarf fyrst að vita hvort eigindin er innan tvöfaldra gæsalappa, einfaldra gæsalappa eða án gæsalappa. Textinn:

<strong>merki</strong>

verður ekki sjálfkrafa að merki inni í value="..."; hann er enn hluti eigindargildisins.

Skoðaðu hráa svarið, ekki aðeins Elements, því vafrinn getur leiðrétt gallað HTML og sýnt aðra skipan en þjónninn sendi. Merki eins og EIGIND_42 hjálpar þér að finna nákvæman stað. Prófaðu síðan aðeins þá afmörkun sem passar við raunverulegar gæsalappir.

Ef gæsalappir og sérstök tákn verða að HTML-einingum eins og &quot;, &lt; og &gt; er einföld brotalokun ólíkleg. Það er dæmi um samhengisbundna úttakskóðun sem CTF-rannsakandi á að þekkja og nota til að færa sig að öðrum svelg.

JavaScript-samhengi

Þjónn getur sett gildi inn í skriftu:

<script>
  const query = "INNTAK";
</script>

Nú er JavaScript-þáttari næsta lag. HTML-kóðun ein svarar ekki öllum reglum JavaScript-strengs; gæsalappir, bakskástrok og línuskipti hafa sérstaka merkingu.

Ekki prófa langan lista af lokunum. Finndu fyrst hvort gildið er í streng, tölu, sniðmátsstreng eða JSON sem er síðan þáttað. Ef þjónninn notar rétt JSON-raðgreiningu og gildið helst inni í tilvitnuðum JSON-streng er einföld lokun ólíkleg.

CSP getur einnig bannað innfelldar skriftur þótt textinn komist út úr strengnum. Console segir þá hvaða tilskipun stöðvaði keyrsluna.

Vefslóðarsamhengi

Inntak getur orðið að href, src eða annarri slóð:

<a href="INNTAK">Opna</a>

Hér skiptir skema, kóðun og gerð einingar máli. Venjuleg https:-slóð er aðeins tengill. Sum virkt skemu geta keyrt kóða þegar notandi virkjar þau, en nútímavafrar og CSP takmarka hegðun eftir samhengi.

Í byrjenda-CTF á verkefnið að gefa skýra vísbendingu ef slóðarsamhengi er ætlað. Ekki smella á óþekktar ytri slóðir eða senda gögn út fyrir labbið. Leitarljós notar einfalt HTML-textasamhengi.

Hvar sést merkið í raun?

Byrjaðu á einstökum texta:

XSS_MARKER_42

Leitaðu að honum á þremur stöðum:

  1. í hráa HTTP-svarinu í Network eða Repeater,
  2. í View Source,
  3. í lifandi DOM í Elements.

Ef merkið er í hráa svarinu er þjónshliðarendurvarp eða geymsla til staðar. Ef það birtist aðeins í Elements hefur JavaScript líklega flutt það. Ef það er HTML-kóðað sem &lt;...&gt; skaltu staðfesta hvort vafrinn sýni bókstaflegan texta.

Þessi þrískiptur samanburður kemur í veg fyrir algenga röng ályktun: að kalla allt sem sést í Elements „reflected XSS“.

CSP sem hindrun og vísbending

Öryggisstefna efnis, CSP, getur takmarkað hvaða skriftur, atburðaeigindir og tengingar vafrinn leyfir. Svarhaus gæti verið:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

Ef innfelld atburðaeigind er stöðvuð birtir Console yfirleitt skýra CSP-villu. Það segir að HTML hafi mögulega verið sprautað en þessi keyrsluleið sé lokuð.

Lestu stefnuna og verkefnisvísbendingar áður en þú prófar annað. Ef ströng stefna leyfir aðeins föst skriftuföll og ekkert verkefnissértækt bendir á hjáleið er XSS líklega ekki ætlað á þeim stað. CSP er viðbótarlag og getur haft eyður, en það er ekki ástæða til að prófa safn af hjáleiðum í hverju verkefni.

Leitarljós hefur ekki CSP sem lokar einföldu staðfestingarmerki. Þannig helst námsmarkmiðið við uppsprettu, samhengi og sama-uppruna beiðni.

HTML-hreinsun og DOMPurify

HTML-hreinsun (e. HTML sanitization) leyfir stundum hluta HTML en fjarlægir virkar einingar, eigindir eða skemu. DOMPurify er algengt JavaScript-bókasafn fyrir slíka hreinsun.

Ef frumkóði sýnir að notandainntak fer í núverandi, rétt stillta DOMPurify og hreinsaða niðurstaðan fer síðan í innerHTML, er XSS á þeim svelg ólíkleg ætluð CTF-leið. Staðfestu með skaðlausu merki að atburðaeigindir séu fjarlægðar og leitaðu svo að öðrum svelg eða öðrum veikleika.

Ekki gera ráð fyrir að hvert verkefni með DOMPurify sé „DOMPurify bypass“. Slíkar hjáleiðir ráðast af útgáfu, stillingum og vafra og eru yfirleitt framhaldsatriði. Verkefnið þarf að gefa skýrar vísbendingar ef gamall eða rangt stilltur hreinsari er sjálft markmiðið.

Mutation XSS í stuttu máli

Vafri getur endurraðað eða leiðrétt HTML þegar það er þáttað og sett aftur í DOM. Í sjaldgæfum tilvikum getur texti sem leit skaðlaus út fyrir hreinsun orðið að virkri skipan eftir slíka umbreytingu. Þetta er kallað mutation XSS, mXSS.

mXSS er háð nákvæmri þáttunarhegðun og á ekki að vera fyrsta tilgáta byrjanda. Ef venjulegt HTML er kóðað, DOMPurify fjarlægir atburðaeigindir og ekkert bendir á endurþáttun skaltu ekki byrja að afrita mXSS-strengi úr gömlum lausnarskrifum.

Áhrif án kökuþjófnaðar

Algeng XSS-líking er að lesa document.cookie, en HttpOnly-kaka er ekki aðgengileg JavaScript. Það þýðir ekki að XSS sé áhrifalaust. Vafrinn getur áfram sent kökuna sjálfkrafa með sama-uppruna beiðnum.

JavaScript sem keyrir á Norðurmarkaðnum getur til dæmis:

  • lesið gögn sem sama gervilota má sjá,
  • sent ástandsbreytandi beiðni í sömu lotu,
  • breytt viðmótinu eða sýnt fölsk skilaboð,
  • og fylgst með texta sem notandinn setur á sömu síðu.

Í Leitarljósi er áhrifamælingin afmörkuð: kóðinn sækir /flag úr sömu gervilotu og birtir svarið staðbundið. Hann les ekki kökuna og sendir ekkert út fyrir verkefnið.

Göngudæmi: Leitarljós

Opnaðu leitina og sendu XSS_MARKER_42. Finndu merkið í hráa svarinu og staðfestu að það lendi milli HTML-merkja. Prófaðu síðan <strong>XSS_MARKER_42</strong> og athugaðu hvort vafrinn býr til strong-einingu.

Næst skaltu nota staðbundna data-xss-breytingu til að staðfesta JavaScript-keyrslu. Settu inntakið í q með DevTools, Burp eða slóð sem vafrinn opnar. Með curl má skoða endurvarpið, en curl keyrir ekki JavaScript:

BASE_URL='https://slod-verkefnisins.example'

curl -i -G "$BASE_URL/search" \
  --data-urlencode 'q=<skaðlaust HTML-próf>'

Þegar keyrslan er staðfest er lokaaðgerðin sama-uppruna fetch():

fetch("/flag")
  .then(response => response.text())
  .then(flag => {
    const output = document.createElement("pre");
    output.textContent = flag;
    document.body.append(output);
  });

Kóðinn sýnir aðeins hvað JavaScriptið á að gera eftir að það keyrir. Þú þarft sjálf/ur að setja hann inn í staðfesta HTML-samhengið án þess að breyta markslóð eða senda gögn annað. Notaðu textContent við birtingu fánans svo hann verði texti, ekki annað HTML-lag.

Ef /flag skilar höfnun skaltu athuga hvort þú opnaðir leitina í sömu gervilotu og forsíðuna. Ef Console sýnir JavaScript-setningafræðivillu skaltu minnka aftur í data-xss-prófið og laga aðeins gæsalappirnar í staðfesta samhengi.

Vísbendingar gegn veikleikum

Merki sem birtist sem &lt;strong&gt; er endurvarp en ekki HTML-túlkun. Eining sem sést í DOM en enginn staðbundinn JavaScript-áhrifamælir breytist staðfestir aðeins HTML-innskoti. CSP-villa getur sýnt að tiltekin atburðaleið er lokuð.

textContent, createTextNode() og rétt samhengisbundin kóðun eru sterkar vísbendingar gegn veikleika á viðkomandi svelg. Nútímalegt DOMPurify sem fjarlægir prófaða virka eigind bendir yfirleitt til að verkefnið ætlist ekki til XSS þar. Færðu þig þá að annarri uppsprettu, öðrum svelg eða öðrum veikleika.

Ef JavaScript keyrir aðeins á síðu sem enginn annar gervinotandi opnar getur áhrif geymds XSS verið bundið við þína eigin lotu. Leitarljós nýtir það viljandi sem kennsluæfingu; verkefni með fórnarlamb eða bot þarf að segja það skýrt.

Samantekt

XSS verður þegar notandastýrt gildi nær frá uppsprettu í svelg þar sem vafrinn túlkar það sem virkt HTML eða JavaScript. Endurvarpað XSS kemur úr núverandi beiðni, geymt XSS birtist síðar og DOM-byggt XSS verður þegar JavaScript flytur gildi í hættulegan DOM-svelg.

HTML-texti, eigind, JavaScript-strengur og vefslóð eru ólík samhengi. Við byrjum á textamerki, staðfestum HTML-túlkun og notum síðan skaðlausa DOM-breytingu sem keyrslumælingu. CSP, samhengisbundin kóðun, textContent og DOMPurify eru hagnýtar vísbendingar um að leita annað, sem geta sparað mikinn tíma.

Kökuþjófnaður er ekki eina áhrifamyndin. Sama-uppruna JavaScript getur sent beiðnir í gervilotu jafnvel þegar lotukakan er HttpOnly. Í næsta kafla skoðum við CSRF, þar sem vafri sendir kökur sjálfkrafa þótt árásarsíðan geti ekki lesið svarið eða keyrt kóða í uppruna markvefsins.