一、Go語言木馬增長顯著
據網絡安全公司 Intezer 報告顯示,惡意軟件的開發(fā)者已經從 C 和 C++ 逐漸轉向 Go 語言,自 2017 年以來,基于 Go 語言的惡意軟件數量呈現(xiàn)爆發(fā)式增長,增幅超過了 2000%。預計Go的使用率在未來幾年將持續(xù)上升,并與C、C++和Python一起,成為惡意軟件編碼的首選編程語言之一。
Go語言木馬的迅猛增長,與Go語言天然的開發(fā)優(yōu)勢有較強聯(lián)系。與其他開發(fā)語言相比,Go的主要優(yōu)勢表現(xiàn)在:
經過情報搜集可以發(fā)現(xiàn),從最近兩年開始,除一般惡意軟件外,某些知名APT惡意軟件Zebrocy、SUNSHUTTLE、HabitsRAT等,也使用Go語言開發(fā)編譯,并且利用TLS協(xié)議實現(xiàn)與C&C服務器之間的加密通信。為了研究在Go語言木馬的加密通信特征并達成有效檢測,我們對Go語言中TLS協(xié)議的實現(xiàn)細節(jié)進行了研究,重點考察TLS協(xié)議握手協(xié)商的獨有特點。文末針對Go語言實現(xiàn)的惡意加密流量檢測給出了實例。
二、Go語言TLS協(xié)議實現(xiàn)分析
TLS(Transport Layer Security)是一個保證信息安全的應用層協(xié)議,它的前身是 SSL。它是一套對由 TCP 傳輸的報文進行加密的協(xié)議。TLS通常用于保證Web通信(HTTP)以及其他流行協(xié)議的安全,比如POP、IMAP,當它們使用TLS保護后,分別被稱為HTTPS、POPS和IMAPS。
我們對Go語言中TLS通信的實現(xiàn)過程進行了研究分析。在Go語言中,實現(xiàn)TLS通信主要分為以下幾步:創(chuàng)建請求、獲取連接、建立TCP連接、實現(xiàn)TLS握手、傳輸加密數據。
2.1外層封裝分析
在Go語言中,HTTPS協(xié)議的實現(xiàn)方法是以HTTP協(xié)議包中實現(xiàn)的,且具有相同的請求流程:
在Go的官方文檔中,共提供了五種實現(xiàn)方法:Get、Head、Post、PostForm和Do。其中前四種方法都調用Do方法,即使直接調用Get、Head、Post或PostForm,內部同樣會創(chuàng)建Request對象,且最終總是通過Client.Do函數發(fā)送請求。下圖為實現(xiàn)Get方法的函數調用順序,可以發(fā)現(xiàn)通過Client.Do()方法調用私有的Client.do()方法來執(zhí)行請求,并且在最后執(zhí)行請求中調用 Transport的 RoundTrip方法。
2.2連接機制分析
在Go中,具有獨特的連接管理機制——連接池。發(fā)送請求時,我們從連接池中獲取連接,請求完畢后再將連接還給連接池;連接池幫我們實現(xiàn)了連接的建立、復用以及回收工作。而在HTTPS中,連接池就是通過調用 Transport的 RoundTrip方法實現(xiàn)的。
連接池機制
在RoundTrip方法中,調用了私有的roundTrip方法,其中通過getConn方法來獲取連接或者建立新的連接。在getConn中,主要有兩步操作,第一步先嘗試從空閑的連接池獲取空閑連接(通過GotConn方法),如果緩存中有空閑的連接則獲取空閑的連接;如果空閑的連接池中沒有可用的連接,則調用dialConn方法來新建連接。
在dialConn方法中,新建連接的大致過程如下:
在第二步創(chuàng)建連接中,對于HTTPS請求,會進行兩步操作,分別建立TCP連接,和TLS連接。其中,dial負責建立TCP連接,實現(xiàn)TCP的四次握手;addTLS負責TLS連接,實現(xiàn)TLS握手。
2.3 TCP握手建立分析
在Go中,網絡編程依舊是依賴socket編程實現(xiàn)的,在net包中有一個TCPConn類型,可以用來建立TCP客戶端和TCP服務端間的通信通道。TCPConn 類型里有兩個主要的方法:Write和Read,用來實現(xiàn)通信時的數據讀寫。
在上一節(jié)我們提到了dial方法,dial方法會調用DialTCP方法來建立一個TCP連接,并返回一個TCPConn類型的對象。在DialTCP方法中,Go通過syscall系統(tǒng)調用,實現(xiàn)了對socket方法的調用。
同樣的,Go使用syscall系統(tǒng)調用實現(xiàn)了bind、connect、send和recv等方法。對于socket編程的初始化方法WSAStartup,Go語言也進行調用,不過調用的位置在執(zhí)行main函數之前的main_init函數中,該函數的主要功能是對需要使用的函數庫進行初始化。
2.4 TLS協(xié)商發(fā)起分析
建立TCP通信通道后,在addTLS方法下通過調用Client方法封裝得到一個tls包下的Conn結構。
在這個封裝過程中會對調用clientHandshake方法來實現(xiàn)TLS握手,其中makeClientHello方法是對TLS握手中的ClientHello進行設置。在makeClientHello方法中,首先會判斷是否有tls.config,也就是檢查是否存在用戶自定義的ClientHello設置,如果不存在則按照默認情況生成,并存儲在clientHelloMsg結構體中。下面針對ClientHello的詳細信息進行分析。
2.4.1密碼套件列表
如果用戶沒有設置自定義密碼套件,Go語言就會使用庫文件中默認的密碼套件。也就是說對于Go中生成的默認TLS通信,密碼套件是固定的,且由庫函數決定。下圖為Go1.17版本中,默認的密碼套件。
在1.13版本Go開始提供對TLSv1.3的支持,并且增加了3個TLSv1.3獨有的密碼套件。
2.4.2默認壓縮方式
默認壓縮方式為無壓縮。
2.4.3隨機數和SessionID
均為隨機生成。
2.4.4擴展項
從實現(xiàn)源碼分析,Go默認提供Status_Request、Signed_Certificate_Timestamp、Supported_Groups、EC_Point_Formats等擴展。根據調用方式的不同,可能會產生Server_Name_Indicator、Supported_Versions、Application_Layer_Protocol_Negotiation等擴展。擴展內容可能與調用時的設置和輸入有關。
2.5實際流量驗證
通過以上的研究,可以確定,Go語言下TLS通信依舊使用了socket網絡編程體系,且是通過syscall系統(tǒng)調用實現(xiàn)的。在不主動對TLS握手特征進行設置時,TLS握手特征是由Go語言的庫文件決定的。經過實驗后,發(fā)現(xiàn)不同平臺下、不同版本(1.11-1.17)的Go語言中默認的clientHello信息基本是不變的。
進一步確定加密套件的變化情況,發(fā)現(xiàn)在1.13版本之前有16個加密套件,1.13版本之后在這16個加密套件不變的基礎上多了3個加密套件。這是由于在1.13版本時新增了對TLSv1.3的支持,導致后續(xù)版本中新增了3個TLSv1.3的密碼套件。
Go語言中TLSv1.2和TLSv1.3的密碼套件
Go語言在默認情況下,壓縮方式會選擇無壓縮,提供Status_Request、Signed_Certificate_Timestamp、Supported_Groups、EC_Point_Formats等擴展項。下圖為默認情況下,1.17版本Go語言產生的TLS流量中,ClientHello的擴展項情況。
Go語言TLS默認擴展項
三、Go語言和C/C++的TLS加密對比
和傳統(tǒng)編程語言C/C++相比,Go語言下TLS協(xié)議完全由自身的封裝庫實現(xiàn),而C/C++語言則需要導入第三方庫來實現(xiàn)。所以,默認情況下,GO語言TLS協(xié)議的握手特征只與Go語言的版本相關,而C/C++語言則是由使用的第三方庫的類型、版本及調用參數決定的。
? | Go語言 | C/C++ |
TLS會話的實現(xiàn)方式 | 通過syscall系統(tǒng)調用socket函數 | 通過Openssl等第三方庫調用socket函數 |
默認密碼套件列表 | 基于Go語言封裝庫,由Go語言的版本決定 | 基于第三方庫,受第三方庫的類型、版本及調用參數影響 |
默認壓縮方式 | 基于Go語言封裝庫,一般為無壓縮方式 | 基于第三方庫,一般為無壓縮方式 |
隨機數和SessionID | 由Go語言封裝庫隨機生成 | 由第三方庫隨機生成 |
擴展項 | 基于Go語言封裝庫,由Go語言的版本決定 | 基于第三方庫,受第三方庫的類型、版本及調用參數影響 |
四、Go語言木馬惡意加密通信檢測分析實例
以Win32.Vobfus.azpd惡意軟件為例,該惡意軟件由Go語言開發(fā)編譯。下圖為Win32.Vobfus.azpd的TLS惡意加密流量檢測情況,可以發(fā)現(xiàn)該流量中,使用了16套加密套件,與Go語言默認使用的加密套件一致;但擴展項為9項,比默認情況下的6項多了3項,說明惡意軟件對擴展項進行了主動設置。最終,觀成瞰云-加密威脅智能檢測系統(tǒng)(ENS)對TLS會話的握手信息評分為0.75,結合證書檢測、行為檢測等因素,綜合評分為0.66,威脅標簽為Win32.Vobfus.azpd。