正兒八經說技術——以Emotet為例深入分析CMD命令混淆技術

發布時間 2018-12-13
Emotet一款著名的銀行木馬,首次出現于2014年年中。該木馬主要通過垃圾郵件的方式傳播感染目標用戶,今年仍然非?;钴S,并且不斷變化傳播花樣,采用越來越復雜的混淆編碼來躲避檢測。
    
CMD和Powershell命令常常被用在惡意軟件中執行惡意腳本文件,并通過腳本混淆、加密或編碼方式來繞過AV檢測。本文列舉兩個典型的Emotet傳播中使用的混淆CMD命令,來深入分析CMD.命令混淆技術。

先看一個從DOC文檔嵌入的VBA宏代碼中提取的CMD命令,乍一看上去,像是無意義的一串字符,仔細分析起來需要先了解一下CMD命令的混淆方式。



 


 壹

CMD命令的混淆方式


 
 插入特殊字符混淆命令 
 
字符“^”是CMD命令中最常見的轉義字符,該字符不影響命令的執行。因為在cmd環境中,有些字符具備特殊功能,如 >、>>表示重定向,| 表示管道,&、&&、|| 表示語句連接。它們都有特定的功能,如果需要把它們作為字符輸出的話,echo >、echo |之類的寫法就會出錯——cmd解釋器會把它們作為具有特殊功能的字符對待,而不會作為普通字符處理,這個時候,就需要對這些特殊字符做轉義處理:在每個特殊字符前加上轉義字符^。

因此,要輸出這些特殊字符,就需要用 echo ^>、echo ^|、echo ^|^|、echo ^^之類的格式來處理。另外,此轉義字符還可以用作續行符號。
 


逗號“,”和分號 “;”可以互換,可以取代命令中的合法空格。多個空格也不影響命令執行。
 


成對的圓括號()也會出現在命令參數中,也不影響命令的執行。圓括號表示嵌入子命令組,同樣被cmd.exe參數處理器進行解釋。如:cmd.exe /c ( ( ((echo Command 1) ) )) &&( ( (((((echo Command 2))))) ) )
 
 利用CMD環境變量拼接命令 
 
Cmd.exe內部命令有: set、assoc ,ftype等。

Set命令用來顯示、設置或刪除cmd.exe環境變量。命令格式:
SET [variable=[string]]
  variable  指定環境變量名。
  string    指定要指派給變量的一系列字符串。

在命令行中輸入 set,會列舉出cmd.exe中所有的環境變量。
 


assoc:文件名擴展關聯命令,用于顯示和設置文件名擴展關聯,可以指定某種后綴名的文件按照特定的類型文件打開或執行。命令格式為:assoc [.ext[=[fileType]]] 

.ext是指:指定要關聯的文件后綴名。點號(.)是不能省略的,如果省略了系統將顯示該后綴名文件的關聯信息。fileType是指:指定相關聯的文件類型。如果只使用該參數,將顯示該文件類型的信息。反之,該命令將列出系統注冊的素有后綴名文件和相關的類型。
 


ftype:顯示或修改用在文件擴展名關聯中的文件類型,指定一種類型的文件默認用哪個程序運行或打開。命令格式為:ftype [fileType[=[openCommandString]]



cmd.exe的環境變量分為系統已有的環境變量和自定義變量。利用環境變量的值中的字符或字符串,可以拼接成黑客需要的cmd命令,同時可以逃避靜態檢測。如系統已有的環境變量%comspec%變量的值默認為:“C:\WINDOWS\system32\cmd.exe”,set命令可以被編碼為: %comspec:~11,1%%comspec:~-1%%comspec:~-13,1%。
 


%VarName:~offset[,length]% 主要用于獲取環境變量VarName的變量值,偏移offset字節之后長度為length個字節。[,length]可省略。



%comspec:~11,1%表示取comspec變量值中的字符,默認下標從0開始,從下標11開始,取一個字符,即為”s”。offset也支持負數,表示反向遍歷字符串的下標。%comspec:~-1%即為“e“,%comspec:~-13,1%即為”t“。如此編碼set命令,可以逃脫靜態檢測”set“命令字符串的檢測機制。

通常我們也可以自定義一個或者多個環境變量,利用環境變量值中的字符,提取并拼接出最終想要的cmd命令。如:
Cmd /C “set envar=net user && call echo %envar%“ 可以拼接出cmd命令:net user
 


也可以定義多個環境變量進行拼接命令串,提高靜態分析的復雜度:
cmd /c “ set envar1=ser&& set envar2=ne&& set envar3=t u&&call echo %envar2%%envar3%%envar1%”
 


cmd命令的“/C”參數,Cmd /C “string”表示:執行字符串string指定的命令,然后終止。
 


而啟用延遲的環境變量擴展,經常使用 cmd.exe的 /V:ON參數,
/V:ON參數啟用時,可以不使用call命令來擴展變量,使用 %var% 或 !var! 來擴展變量,!var!可以用來代替%var%,也就是可以使用感嘆號字符來替代運行時的環境變量值。后面介紹For循環時會需要開啟/V:參數延遲變量擴展方式。
 

 
 利用For循環拼接命令 
 
For循環經常被用來混淆處理cmd命令,使得cmd命令看起來復雜且難以檢測。最常用的For循環參數有 /L,/F參數。
 


FOR 參數 %變量名 IN (相關文件或命令) DO 執行的命令

FOR %variable IN (set) DO command [command-parameters]

%variable 指定一個單一字母可替換的參數。 這個變量名可以是小寫a-z或者大寫A-Z,區分大小寫,FOR會把每個讀取到的值賦給該變量。在批處理文件中,引用變量要用%%variable,我們這里主要介紹在cmd窗口中,引用變量用%variable即可。
(set)      指定一個或一組文件??梢允褂猛ㄅ浞?。 相關的文件或命令。
command    指定對每個文件執行的命令。 
command-parameters 
             為特定命令指定參數或命令行開關。
/L 參數: 迭代數值范圍
for /L %variable in (start,step,end) do command [command-parameters]

該命令表示以增量形式從開始到結束的一個數字序列。使用迭代變量設置起始值(start),然后逐步執行一組范圍的值,直到該值超過所設置的終止值 (end)。/L 將通過對start與end進行比較來執行迭代變量。如果start小于end,就會執行該命令,否則命令解釋程序退出此循環。還可以使用負的 step以遞減數值的方式逐步執行此范圍內的值。例如,(1,1,5) 生成序列 1 2 3 4 5,而 (5,-1,1) 則生成序列 (5 4 3 2 1)。命令cmd /C “for /L %i in (1,1,5) do start cmd”,會執行打開5個cmd窗口。
 


/F參數: 是最強大的命令,用來處理文件和一些命令的輸出結果。
FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
(file-set) 為文件名,for會依次將file-set中的文件打開,并且在進行到下一個文件之前將每個文件讀取到內存,按照每一行分成一個一個的元素,忽略空白行。
("string")代表字符串,('command')代表命令。
假如文件aa.txt中有如下內容:
第1行第1列 第1行第2列 
第2行第1列 第2行第2列
要想讀出aa.txt中的內容,可以用for /F %i in (aa.txt) do echo %i ,如果去掉/F參數則只會輸出aa.txt,并不會讀取其中的內容。



先從括號執行,因為含有參數/F,所以for會先打開aa.txt,然后讀出aa.txt里面的所有內容,把它作為一個集合,并且以每一行作為一個元素。由上圖可見,并沒有輸出第二列的內容,原因是如果沒有指定“delims=符號列表”這個開關,那么for /F語句會默認以空格鍵或Tab鍵作為分隔符。For /F是以行為單位來處理文本文件的,如果我們想把每一行再分解成更小的內容,就使用delims和tokens選項。delims用來告訴for每一行用什么作為分隔符,默認分隔符是空格和Tab鍵。for /F “delims= “ %i in (aa.txt) do echo %i ,將delims設置為空格,是將每個元素以空格分割,默認只取分割之后的第一個元素。如果我們想得到第二列數據,就要用到tokens=2,來指定通過delims將每一行分成更小的元素時,要取出哪一個或哪幾個元素:for /F “tokens=2 delims= “ %i in (aa.txt) do echo %i。
 

 貳
實際樣本分析
 
我們選取新近的Emotet樣本下載利用的CMD命令混淆,來利用前面的知識來解混淆。
 
 利用自定義環境變量和For循環混淆 
 


該樣本中利用了cmd.exe 的啟用延遲環境變量/V:ON參數,/C參數,利用set命令自定義一個環境變量kpx=lHUwrRfzapaiNzCqHfu:Doc(4YQ0S.1,xk}$) s6dK=mn5/+ygbW-TeP\v2tj{78Mh@;BO'FZ,通過&&拼接命令,然后是個for循環: for %G in (數列)do set     1q=!1q!!kpx:~  %G,    1!&& if %G==  81  call  %1q:~    -377%。我們著重分析下for命令。因為前面使用了延遲環境變量,所以可以使用!1q!!kpx:~  %G,    1!的方式來擴展變量,在運行時代替環境變量值。for的循環變量是%G,%G in (數列值),!kpx:~ %G, 1!表示取環境變量kpx中下標為%G的一個字符,我們可以用如下python編碼實現該功能。數列中的空格可以忽略,數列中的數值正好是377個,kpx字符串的長度是72個字符,下標為81已經不存在,所以當下標%G==81時,運行時環境變量1q=!1q!powershell ……, call %1q:~-377%,所以取1q變量的-377下標正好是for循環遍歷出的powershell……命令,前面的1q=!1q!是初始化變量1q,需要被去掉以免影響正常命令的執行,所以取1q變量的-377下標正好繞過前面的!1q!。
 


輸出:



下載Emotet的鏈接為:
http://catbayouthaction.com/jKS86a
http://spsystems24.ru/O
http://xn--80abdh8aeoadtg.xn--p1ai/multimedia/hD4lyk7
http://borsehung.pro/pfWq
http://inpart-auto.ru/x2bu

 利用cmd系統環境變量和For循環混淆 



先將混淆cmd命令中的轉義字符“^”全部去掉,再將除了變量@之外的逗號“,”、分號“;”、多余空格刪除。注意保留變量@中的逗號和分號,否則影響輸出結果。

 可見利用了cmd的系統環境變量%comspec%,即是cmd.exe的執行路徑。利用For循環的F參數,在命令'aSsoC .cmd'中以字符v、f、=為分隔符,取第二列即是“cmd”。
fOr  /f  " delims=vf=  tokens=2"  %f  IN  ( 'aSsoC  .cmd' ) dO  %f  。其他無意義的字符串會被cmd忽略。
 


接著自定義了一個環境變量@,等于一個1460長度的字符串。然后利用For循環的/L參數,遍歷變量@:FOr /L %s In (1459,-4,+3 ) do (( ( (( seT \=!\!!@ :~ %s, 1!))))& iF %s eQU 3 (((CaLl %\ :~ -365% ),自定義了環境變量“\”,利用環境變量擴展符號!,!@ :~ %s, 1!表示循環變量%s從1459開始,步長為-4,到3結束,循環提取變量@中的一個字符,長度為365個字符,即從For循環重組出的命令開始執行。
 


我們編寫python腳本實現For循環功能:



最終解密出可讀的內嵌powershell命令:




下載Emotet的鏈接為:

http://reitmaier.de/01cedmfXo
http://phoxart.com/sWP0E9
http://panbras.com.br/FHhUYIQ
http://osmanager.com.br/t3HnvWx9x
http://oldwillysforum.com/ChleCkW

 
總結
 
CMD的命令混淆千變萬化,唯一的目的就是逃避沙箱的靜態或動態檢測,增加分析難度。萬變不離其宗,只要掌握了cmd命令的基本語法規則并熟練使用,目前惡意樣本的各種cmd混淆命令都可以迎刃而解,進而實現對該類樣本的識別檢測和防范。
 
參考: 
https://www.fireeye.com/content/dam/fireeye-www/blog/pdfs/dosfuscation-report.pdf