蟻劍改造過WAF系列(一)


Author: lz520520@深藍攻防實驗室

0x00 前言

  為什么會有修改蟻劍這個念頭呢,之前有朋友對冰蠍做了改造,本來冰蠍就是加密傳輸,可對抗大多數WAF,但因為有一些弱特征、強特征等,通過流量分析轉換成自動化檢測,也可以做到冰蠍流量檢測的,比如密鑰交互特征,但改造之后,基本是無法檢測的,跳過了密鑰交互這個步驟,將AES密鑰寫死,還有其他新增的機制不僅僅對抗WAF,還可以對抗人工分析。
  冰蠍改造需要反編譯源碼,然后先修bug,再開始改造,比較麻煩,成本也比較高,而蟻劍作為一款開源的webshell管理工具,讓二次開發更容易,並且使用自帶的加載器就可以調試;有編碼器和解碼器模塊,可以讓使用者很容易做到流量混淆,所以我也在想蟻劍是否也可以達到冰蠍一樣的效果。
  第一篇我會介紹一下后續改造所涉及到的蟻劍模塊,這樣讓大家能更容易理解,后續也方便自己去動手開發屬於自己的蟻劍。

0x01 蟻劍介紹

  我不會長篇大論蟻劍各個組成部分,只是挑重點講,想了解詳情的可以看下蟻劍官方文檔(https://doc.u0u.us/zh-hans/index.html) ,這個文檔需要代理訪問。
首先了解下蟻劍分為兩部分,加載器和源代碼,源代碼是nodejs,使用加載器就可以讓你不用安裝nodejs的環境即可運行和調試蟻劍,后續關於蟻劍的調試工作基本使用加載器antsword.exe來完成,而我們改造的部分只有antsword-master(源代碼)。

  如果有用過蟻劍的,都會知道他的編碼器和解碼器,這個功能也是蟻劍愛好者最喜歡的部分。

  總所周知,蟻劍所有腳本的源代碼(php/asp/aspx等)均來自菜刀,所以流量特征也和菜刀差不多,過WAF基本不用想了。
  所以有了編碼器和解碼器,進行流量混淆可繞過WAF,並且編碼器和解碼器可以自定義,使用nodejs編寫即可,類似於插件很容易上手,先簡單說明下編碼器和解碼器的作用。
  編碼器:對發送流量進行編碼,服務端進行解碼。
  解碼器:服務端對返回流量編碼,需要客戶端通過解碼器解碼還原流量接收。

  可以看到一個對發送加密,一個對返回加密,這樣才能達到完美過WAF的目的,但原版蟻劍會有一個問題,只支持php的編碼解碼,所以需要改造蟻劍支持asp、aspx。
  而jsp這個比較麻煩,他不是腳本語言,沒有eval/execute等函數可以用來直接執行代碼,使用過菜刀的都知道,jsp的"一句話"非常大,如果要達到冰蠍那樣短小的webshell,並且實現加密,得使用冰蠍的方式來實現,類加載,將命令執行、文件管理等操作編譯成class,硬編碼到蟻劍里,然后傳給服務端解析,工作量比較大,當然有人已經實現了蟻劍版的jsp一句話,但只做到了base64和hex編碼發送,有人特意分析還是有比較明顯特征的,而且返回包也沒有做加密處理,這個后續會說到。
  這邊和大家分享幾篇文章,有涉及到蟻劍的流量分析和編碼器編寫等,並且官方github上也分享了幾個編碼器,包括AES/RSA加密的。
  蟻劍流量分析到改裝蟻劍之waf繞過 https://www.t00ls.net/articles-50892.html
  從靜態到動態打造一款免殺的antSword(蟻劍) https://xz.aliyun.com/t/4000
  編碼器、解碼器 https://github.com/AntSwordProject/AwesomeEncoder

  除此之外,蟻劍自帶的編碼器其實過WAF還是比較勉強的,因為他的webshell腳本是最原始的一句話,如

<?php eval($_POST["test"]);?>

  根據上面提到的編碼器作用,可以知道,服務端得進行解碼才能正常接收,那像這種一句話是沒法解碼的,所以實際上是將解碼函數一同發送到服務端,那幾個解碼函數是沒法加密的,就是一個很明顯的特征。

0x02 編碼模塊

  這里使用base64編碼器舉例,通過流量分析可以看到,將原本傳輸的代碼做了base64編碼,然后調用eval對前面的參數進行解碼還原。

  我們再來看看他的編碼器是怎么寫的。在講內容之前,我先跟大家講下編碼器的輸入輸出是怎樣的。
如下

輸入三個參數
pwd:連接密碼,類型string。
data:傳輸的數據數組,類型string數組,。
ext:一些擴展選項,在一些場景可能會用到。

輸出一個參數
data: 編碼器處理后的數組,這個你可以通過代理查看post提交了哪些數據,和這里的data是完全一樣的。

  這里使用console.log來打印查看下編碼之前的這些參數具體數值。
  data數組實際上只有一對鍵值,data['_'] = "……";
  ext有一些選項,比如ext.opts里包含編解碼器名稱、當前編碼語言、密碼、url等等。

  所以base64編碼器在data[pwd]設置為base64解碼代碼,data[randomID]設置為原代碼的base64編碼數據,最后刪除掉data里的原有payload data['_']

  實際上你編碼器選擇default也是會做處理的,只是這個寫死在源碼里,source\core\base.js
也是會將data['_']刪除。

  通過上面例子應該對編碼器的原理有所了解了。
  到此,我相信大家應該會有思路怎么讓發送流量完全加密了,原本自帶編碼器是通過再傳遞解碼代碼來實現無需服務端修改也能編碼傳輸原始數據的目的。
  那么我們只需要將解碼代碼放到webshell里,不就可以實現完全編碼傳輸了嗎。
  我們對一句話改造下

<?php eval(base64_decode($_POST["test"]));?>

  然后修改下編碼器

  最后就可以實現如下的效果,省去解碼代碼傳遞的這一步,當然base64只是舉個例子,你可以DIY加密方式,webshell里寫好相應的解碼函數即可。

0x03 解碼模塊

  我們仔細觀察可以看到返回數據還是明文,所以就需要使用解碼器來對返回加密。
  先看下解碼器的組成,這里導出了兩個方法,asoutput及decode_buff。
  asoutput無需傳入參數,返回一段php代碼字符串,名稱為asenc的函數,這個函數會放在請求包里,用於在服務端執行完代碼后,再回顯部分調用該函數asenc來編碼處理,所以服務端無需針對解碼做改動。

  先看下原始發送的php代碼是啥樣的,第一個紅框就是我們可控的部分,在傳輸之前,蟻劍會將該部分替換成我們編寫的解碼器里的php編碼函數,然后再第二個紅框處調用asenc函數進行編碼。

  decode_buff 是在接收時進行解碼操作,傳入的data為buffer數組,返回也是buffer數組,這里一般不需要做編碼猜解,因為后續處理代碼會執行該操作,ext是一個擴展選項,有包括密碼等選項,可擴展操作,比如需要有key進行解碼。

  如上編寫完畢后,並選擇好解碼器,進行測試,效果如下,可以看到返回流量也進行了編碼加密,並且返回部分有前后分界字符串,只有客戶端知道分界位置,WAF基本上是沒法定位位置進行解碼的。

  大家可以看到簡單的設置編碼器和解碼器,就可以實現個加密傳輸的效果,如果一些WAF無法有效識別base64解碼,基本上就可以繞過了,當然我們要保證萬無一失,就需要使用復雜點的函數加密。

  上面這個看起來都加密了,實際上並不是,這只是一個探針式的請求,來校驗webshell是否連接成功。
在實際文件管理和命令執行過程中,會提交更多參數,這也就是為啥編碼器里的data是一個數組,菜刀本身也是這么設計的。
比如我執行了一個命令,可以看到多傳輸了兩個參數,這兩個參數base64編碼了,但這個編碼是固定不變的。

  base64解碼如下

  所以如果WAF支持base64解碼,我相信大多數WAF是做得到的,那你傳輸了執行命令直接都被檢測到了。
  那么解決思路有幾種,
  一、在編碼器里將其他參數也進行相應編碼(這里傳遞到編碼器過來的時候已經是預處理base64編碼了),服務端對這些參數進行解碼,然而這些參數值實際上被服務端取出來的方式是通過一個獲取請求參數列表的函數,比如PHP里的$_POST,asp里的request,aspx里的Request.Item,這個提取的代碼是放在test參數值里的,那么解碼函數得通過編碼器,將test傳參里的"請求獲取函數"替換成對請求值額外解碼的函數。是不是聽着有點繞。
  看圖說話,也就是需要將下面這個base64解碼之前先對POST提交數據進行解碼。

  修改成這樣

  如果按照原來的編碼器方式,就是需要進行正則替換這部分,而且不同語言函數不一樣,所以需要不同的操作,這個通用性不強,每次新的編碼器都需要做這么一步,當然你如果打造一個像冰蠍那樣固定的webshell,編碼器固定不變也是沒問題的。

  這里發散下,如果像解碼器那樣,有一個asenc一樣的asdec的函數,你去設置一個解碼函數,來解碼其他被編碼傳輸的參數。這就需要改動他請求模塊代碼,這是一個思路。
  另一個思路,對請求代碼改造,不要分多個參數傳遞,都是一個參數提交,就像返回值就一串,這個工作量就會比較大,需要修改請求代碼,並且各個語言的代碼模板也得都換成新的了。
  還有一個思路,沿着第一方式,設置一個asdec的解碼函數,但既不需要自己去每次編寫一個解碼函數,又可控調整傳參混淆的目的,這個就是后面實現的方式,達到所有傳參都混淆的,這里簡單提一下,就是為每個參數值添加一個隨機長度前綴,在解碼的時候去掉前綴即可,而我們可控的選項就是前綴的長度,在UI上添加一個前綴長度設置項,可填寫自定義長度。這個方法既簡單又好用,因為加隨機前綴,就可以導致WAF無法正常解碼,他也不可能知道從哪分割獲取正常的參數值。這個相較其他方法,改造起來不要太簡單,並且我在這個基礎上,再進行了一次參數值倒序,使用一個倒序函數就可以還原了。
  為啥這么做是防止意外,因為比如base64,你如果填充是4的整數倍,是可以正常解碼的,如果是hex編碼,你填充偶數也是可以正常解碼的,這里不細講,沒明白的可以琢磨一下。

  除了上面這種解決思路
  對其他參數編碼后,在不改動test的值的情況下,服務端可以提前解碼替換掉"請求參數獲取"的列表,這個在PHP里還是比較好處理的,就是將$_POST里的值進行解碼然后替換$_POST,這樣后續主代碼使用$_POST也就能獲取正常的值了。但對asp/aspx不適用,並沒有像PHP這樣的預定義變量,無法覆蓋request函數里獲取到的值,這個我沒有細究,可能也有方法,有人知道的話也可以一起討論下。

0x04 編碼模塊小結

  到這里,我們對編碼器和解碼器有了一定了解,可以上手改造自己的蟻劍了。
  編碼器里,使用nodejs對發送數據進行編碼,webshell在服務端進行解碼。
  解碼器里,使用nodejs對接收數據進行解碼,傳遞腳本代碼使得webshell在服務端進行回顯數據編碼。
  所以,編碼與解碼都需要客戶端和服務端的配合,最終實現加密傳輸的效果。
  冰蠍改造將動態的AES密鑰寫死在webshell里,可以減少很多特征。蟻劍就是將解碼函數寫死在webshell里,也可以有效繞過WAF檢測。
  這個部分其實算是插件改造,核心代碼部分還沒動,比如上面說的隨機前綴設置,以及asp、aspx的解碼模塊支持,本篇先編解碼器,讓大家可以先動起手來,后面再進行源碼分析,進一步改造蟻劍。

0x05 參考

https://www.freebuf.com/sectool/98681.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM