記一次流量分析&代碼分析-冰蝎v2.0.1
一、流量分析
1.簡單介紹
冰蝎流程圖如下:
冰蝎是先請求服務端,服務端判斷請求之后生成一個128位的隨機數,并將這個128位的隨機數寫入到session里面,并將這個128位的隨機數返回給客戶端,但是客戶端并不會使用這個key作為之后的通訊的key,而是會繼續重復上面過程,不斷獲取key,直到滿足特定條件之后,才會確定是最終的key。客戶端會保存這個key和響應報文里面的set-cookie的值。這個key就是之后客戶端和服務端進行通訊的密匙。

2.冰蝎通信分析
冰蝎通信大致分為兩個階段。
第一階段:
Attacker通過GET方法或者POST方法形如下圖這樣請求服務器密鑰。

服務器使用隨機數MD5的高16位作為密鑰,存儲到會話的SESSIONID中,并返回密鑰給attacker。密鑰可通過wireshark抓包看到,見下圖。
在wireshark中即可捕獲到冰蝎客戶端與服務器通信的數據包。


第二階段:
1.客戶端把待執行命令作為輸入,利用AES算法或XOR運算進行加密,并發送至服務端;
2.服務端接受密文后進行AES解密或者XOR運算解密,執行解密后的命令
3.執行結果通過AES加密后返回給attacker
流量分析過程:
1.追蹤HTTP流即可看到冰蝎客戶端與服務器的完整通信過程。

2、分析冰蝎與服務器的通信過程。

3.當冰蝎第一次訪問服務器webshell時,以GET方式提交隨機數字,因此服務器將會生成16位的隨機字符串,寫入session后通過print函數將密鑰返回客戶端,冰蝎第二次訪問服務器時以相同方式更新密鑰。

二、冰蝎流量解密
(一) 解密思路
在服務器開啟 PHP OpenSSL 擴展腳本的情況下,冰蝎密文采用對稱加密算法AES加密,加密后還會進行 base64 編碼。在客戶端發起密鑰協商后,會返回16位明文隨機密鑰。
(二) 工具
AES在線解碼 (http://tools.bugscaner.com/cryptoaes/)
BASE64在線解碼(https://tool.oschina.net/encrypt?type=3)
經過一次密鑰產生與一次密鑰更新后,雙方開始以對稱密鑰進行加密通信,首先是冰蝎向服務器發送加密數據,而解密函數在webshell中:

我們通過解密方式解密請求包數據:

(三)解密過程
1. 獲取密鑰
從請求密鑰的數據包中獲取密鑰:29ab481053a0ebeb
2. 獲取請求密文、返回密文
3. 借助在線解密解碼平臺
(1)輸入密鑰和請求密文,解密后為 base64 編碼;base64解碼
(2)接著進行解密喝解碼
(3)輸入密鑰和返回密文,解密后為base64編碼;base64解碼

梳理一下流程:
【1】冰蝎將AES加密數據發送至服務器webshell;
【2】webshell檢測到沒有以GET方式提交的pass參數的值,因此調用openssl擴展,結合冰蝎發送的數據和session中的密鑰進行AES解密,得到解密后的載荷assert|eval(base64_decode('QGVy......;
【3】以|為分隔符,將解密后的載荷分隔為兩部分,將執行載荷的過程放入自定義類C的魔術函數__construct()中;
【4】實例化自定義類,觸發__construct()函數,執行eval(base64_decode('QGVy......;
【5】執行時,首先進行base64解碼,然后執行解碼后的代碼,代碼中定義了result數組,result數組有兩個鍵值對:
'status':base64_encode("success")
'msg':base64_encode("57178bd5-7c2a-4c58-a6e8-56e2a2cf6223")
【6】然后從session中取得密鑰,對result數組加密,將加密結果輸出到客戶端;
(4)冰蝎收到加密數據后,在內部進行解密(解密代碼與解密的過程在冰蝎內部完成,我們看不到)
但是我們可以自行解密,查看服務器發送至冰蝎的真實數據:
三、冰蝎特征檢測
總結冰蝎在流量交互中的特征,這些特征可分為兩類。一類是可繞過特征,這類特征攻擊者可通過構造報文進行繞過,致使設備檢測不到冰蝎 webshell 特征。另一類是非可繞過特征,攻擊者在某些情景無法更改 HTTP 某些字段,致使有固定報文字段可供設備檢測。使用單個特征誤報較高,但多個特征配合使用可降低誤報,推薦多個特征搭配使用,進一步提升特征檢測的準確性。
(一) 可繞過特征
1. Accept字段
Accept是HTTP協議常用的字段,但冰蝎默認 Accept 字段的值卻很特殊,這個特征存在于冰蝎的任何一個通訊階段。如下:
Accept: text/html,image/gif, image/jpeg, *; q=.2, */*; q=.2
請求體Accept字段

2. UserAgent字段
冰蝎內置了十余種 UserAgent ,每次連接 shell 會隨機選擇一個進行使用。如果發現歷史流量中同一個源IP訪問某個URL時,命中了以下列表中多個 UserAgent ,可基本確認為冰蝎特征。

3. 長連接
冰蝎通訊默認使用長連接,避免了頻繁的握手造成的資源開銷。默認情況下,請求頭和響應頭里會帶有 Connection。
Connection: Keep-Alive

(二) 非可繞過特征
1. 密鑰傳遞時URL參數
密鑰傳遞時,URI只有一個參數,key-value型參數,只有一個參數。Key是黑客給shell設置的密碼,一般為10位以下字母和數字,很少有人設置特殊字符做一句話密碼的(少數情況我們不考慮)。而Value一般是2至3位隨機純數字。另外webshell的擴展名一般為可執行腳本,如下:
\.(php|jsp|asp|aspx)\?(\w){1,10}=\d{2,3} HTTP/1.1

2. 加密時的URL參數
在加密通訊過程中,無URL參數。如下:
\.(php|jsp|asp|jspx|asa) HTTP/1.1
3. 傳遞的密鑰
加密所用密鑰是長度為16的隨機字符串,小寫字母+數字組成。密鑰傳遞階段,密鑰存在于Response Body中。正則如下:
^[a-fA-F0-9]{16}$
4.加密數據上行
在加密通訊時,php/jsp shell 會提交base64編碼后的請求數據。用如下正則便可以很好的匹配。數字20是指定的字符出現至少20個才會匹配。正則如下:
\r\r[a-zA-Z\d\+\/]{20,}
5. 密數據下行
該特征同樣存在于加密通訊時,在返回包中的數據是加密后的二進制數據。這里使用正則的“非”匹配二進制非常見字符。正則如下:
” [^\w\s><=\-‘”\:\;\,\!\(\)\{\}][\w]{2}[^\w\s><=\-‘”\.\:\;\,\!\(\)\{\}][a-zA-Z\d]{2}”
四、冰蝎源碼分析
主要過程就是獲取key和保存cookie之后,獲取服務端信息,執行命令,文件操作,數據庫操作等都是使用這個key和cookie進行操作,對執行的代碼動態生成class字節數組,然后使用key進行aes加密,再進行base64編碼,并用post方式發送數據。接收服務端返回的數據時,先使用key進行解密,解密之后的數據一般是使用了base64編碼,解碼之后就可以獲取服務端返回的明文數據。
反編譯Behinder.jar包,其核心代碼在net.rebeyond.behinder包內

主要源代碼:

1.上傳原理
冰蝎的原理為上傳一個class字節碼,通過調用classloader的defineClass方法,將class字節碼,轉換為Class。實例化上傳的這個類,通過equal方法傳遞PageContext。獲取到PageContext,間接獲取到Request、Response、Seesion等對象。如HttpServletRequest request=(HttpServletRequest) pageContext.getRequest()
2.核心代碼分析
其核心部分是協商密鑰getKeyAndCookie
客戶端打開和服務端的連接之后,會先調用BasicInfoUtils類,在BasicInfoUtils類的getBasicInfo方法里,會調用ShellService的構造方法新建ShellService類。而在ShellService類里面的構造方法,會調用Utils的getKeyAndCookie方法。


Utils.getKeyAndCookie方法:
判斷得到的密匙rawKey_1之后,進入循環調用getRawKey方法,并獲取rawKey_2,并且將rawKey_1和rawKey_2進行異或操作(
如果rawKey_1和rawKey_2兩個值不相同,則異或結果為1。如果rawKey_1和rawKey_2兩個值相同,異或結果為0。
)。獲取rawKey_2的方法和獲取rawKey_1基本是一樣的。

獲取服務器基本信息getBasicInfo
在獲取了cookie和key之后,BasicInfoUtil的getBasicInfo就會調用ShellService的getBasicInfo方法來獲取放了木馬的服務器的基本信息

生成paylaod

由于是jsp,獲取對應的字節內容先AES加密,再base64編碼返回下

就是直接說的利用POST發送payload了然后返回執行結果,分析完畢

3.代碼包分類
core包
冰蝎的主要邏輯代碼。
Crypt.java和Decrypt.java 涉及到server端語言的加密解密
Params.java 調用不同語言的payload,返回其字節序列

注意:
如果攻擊者發送的請求不是文本格式的源代碼,而是編譯之后的字節碼(比如java環境下直接向服務器端發送class二進制文件),字節碼是一堆二進制數據流,不存在參數;攻擊者把二進制字節碼進行加密,防御者看到的就是一堆加了密的二進制數據流;攻擊者多次執行同樣的操作,采用不同的密鑰加密,即使是同樣的payload,防御者看到的請求數據也不一樣,這樣防御者便無法通過流量分析來提取規則。
Params類

要讓服務端有動態地將字節流解析成Class,但Java并沒有提供直接解析class字節數組的接口。不過classloader內部實現了一個protected的defineClass方法,可以將byte[]直接轉換為Class。也定義了一個類t繼承classloader,調用時返回父類的defineClass方法,就解決了protected的限制,返回解析后的Class。當然這里用反射機制來實現也是可以的。這樣就可以直接動態解析并執行編譯好的class字節流了,但這里就必須提到java的asm框架。
ASM 是一個 Java 字節碼操控框架。它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進制 class 文件,也可以在類被加載入 Java 虛擬機之前動態改變類行為。

需要執行的命令是編碼在class文件中的,因為class是已編譯好的文件。但不能每次執行命令時都編譯payload,那么就需要payload可以接收參數。為了參數被waf攔截,自然也是二進制流,所以使用ASM框架來動態修改class文件中的屬性值。從上面的代碼就可以看出,只需要向getParamedClass方法傳遞payload類名、參數列表即可獲得經過參數化的payload class。
我們接著看php的代碼實現。

而且.net,asp的實現都是類似的,只是細節上有所不同,而.net的實現是使用編譯好的dll文件。

在payload中的代碼就無法查看
PluginUtils類

ShellEntity類

Crypt(加密類)
在Crypt類中分別有對應的不同server端語言的加解密方法。
繼續看各自的加解密方法,這里以C#的為例。

接著看php和asp的加密方法,根據類型不同加密,進行不同的加密。
在客戶端集成了不同語言shell的管理,所以就復用了部分代碼,下面php執行異或操作的操作就是直接調用asp的代碼實現的。

冰蝎在通信過程中使用AES加密,Java和.Net默認支持AES加密,php環境中需要開啟openssl擴展,v2.0更新以后,PHP環境加密方式根據服務器端支持情況動態選擇,不再依賴openssl,所以在上面代碼中不支持AES的情況就直接調用asp的代碼。
五、冰蝎特征總結
檢測請求方式:Request.method= GET
檢測請求資源:Request.url= /[\w.]*.[a-zA-Z]{3,4}\?\w{0,20}=\d{0,10}
檢測服務端響應(僅16位密鑰):Response.body.startwith =\w{16}
在實際檢測中,需要注意如:shell的名字、后綴(可能利用解析漏洞等)、密碼的長度等等,實際在目前的版本中都存在獲取密鑰這個請求。
V1.0請求包詳情如下:

V2.0.1請求包詳情如下:

冰蝎檢測的難點在于在通信過程中數據通過加密傳輸,難以提取特征,下面我們比較一下兩個版本之間在連接過程中發起的GET請求。
冰蝎2默認Accept字段的值很特殊,而且每個階段都一樣。
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Agent字段
冰蝎內置了十余種 UserAgent ,每次連接 shell 會隨機選擇一個進行使用。但都是比較老的,容易被檢測到,但是可以在burp中修改ua頭。
c.Content-Length
Content-Length: 16, 16就是冰蝎2連接的特征。
REF:
https://zgao.top/
https://xz.aliyun.com/t/7300