近日,Xshell 官方發布公告稱其軟件中存在后門。我們的實習生同學對該后門進行了詳細的分析,確認這是一個具備惡意代碼下載執行和數據回傳等能力的高級木馬。
后門代碼存在于 nssock2.dll
中,采用了多層加密 shellcode、花指令、線程注入等各種方式逃避殺軟查殺和對抗人工分析。總體流程如下
通過 BinDiff 跟最新版的 nssock2.dll
比較可以很容易的發現一個解密 shellcode 的函數
去掉花指令分析,進入到shellcode后主要功能是先查詢 HKCU\SOFTWARE\%d
或 HKLM\SOFTWARE\%d
下的 Data
值是否存在,%d
是把硬盤的序列號異或0xD592FC92
如果Data
值存在就用其中的 key 解密第二層的 shellcode 并執行,反之就會發送 DNS 請求獲取配置信息存儲到 Data
鍵再解密第二層的 shellcode 并執行
獲取配置信息時首先通過根據當前年月的 DGA(域名生成算法)生成一個域名,其算法如下
然后會根據 GUID、主機名和用戶名等信息生成一個前綴進行加密與之前生成的域名拼接后發送 DNS 請求并獲取配置信息
加密主要分兩步,第一步如下
第二步如下
DNS服務器為8.8.8.8
、8.8.4.4
、4.2.2.1
、4.2.2.2
和當前主機的 DNS 服務器,接收到 key 后解密第二層 shellcode 的代碼如下
這里非常有意思,算法跟從dll進入第一層 shellcode 時的解密算法一致,想到CTF的套路嘗試設為相同的 key,key1為0xC9BED351
,key2為0xA85DA1C9
,然后就成功解密出了第二層 shellcode。
根據卡巴斯基的報告,第二層 shellcode 為 Root
插件,入口函數很像 DllMain
主要功能是先設置異常處理函數,并會把異常記錄到 %ALLUSERSPROFILE%\error.log
,然后初始化函數指針表(會在其他插件中被調用),并加載5個插件
動態調試步入 load_plugin
函數就能把5個插件的 shellcode dump 出來,加載完5個插件后會調用ID為 103
的插件(Install)的第二個函數
主要功能是先修改當前進程權限,再調用ID為 102
的插件(Config)的第二個函數
另外還會用 winlogon.exe
進程的權限創建 svchost.exe
進程進行線程注入,調試線程注入的 shellcode 可以先在VirtualAllocEx
后下斷獲取到相應進程中的虛擬地址,然后在 ResumeThread
時下斷,中斷后附加相應進程并在之前獲取的虛擬地址處下斷,執行 ResumeThread
后會在之前的虛擬地址處中斷,之后就可以繼續調試了,初步分析注入的shellcode就是 Root
插件
根據磁盤序列號創建互斥體:Global\% 16-48 random latin characters%
主要功能是監聽根據磁盤序列號生成的注冊表項 HKLM
或 HKCU\SOFTWARE\Microsoft\%5-12 random characters%
監聽到有值改變后會解密并校驗是否是合法的插件并加載和初始化
此插件主要是跟配置信息的讀寫相關,其路徑根據磁盤序列號生成,本機是 C:\ProgramData\MQGOMQQ\TOYMWGMQ\UMGSAIE\DIWEYK
,在每次初始化插件時都會被重寫
默認的 C&C
地址是 dns://www.notped.com
此插件主要是跟 C&C
服務器通信并把命令分發到相應的插件執行,首先根據協議類型選擇發送請求的插件
如果是 URL
就會向根據年月的DGA生成的域名發送 HTTP
請求來得到真正的 C&C
服務器地址
另外此插件也會收集更詳細的主機信息,依次調用 GetSystemTime
、gethostbyname
、GlobalMemroryStatusEx
、GetNativeSystemInfo
、GetDiskFreeSpaceExA
、EnumDisplaySettingsW
、GetSystemDefaultLCID
、QueryPerformanceFrequency
、QueryPerformanceCounter
、GetCurrentProcessId
、RtlGetVersion
、GetSystemMetrics
、GetNetworkParams
和GetAccountSid
此插件主要是用于基于 DNS 協議的 C&C
通信
此后門用了多種手段來增加分析難度,是一個基于插件的完善的攻擊平臺,請盡快升級到最新版本。以上分析如有謬誤之處,歡迎斧正。
from idaapi import *
from ctypes import *
addr = 0x274DFC8
seed = c_uint(Byte(addr) | (Byte(addr + 1) << 8))
result = [None] * 4096
for i in range(4090):
result[i] = chr((seed.value & 0xff) ^ Byte(addr + 2 + i))
seed = c_uint(c_uint(c_uint(0x41120000 * seed.value).value - c_uint(0x434CBEEE * (seed.value >> 16)).value).value - 0x2F878E0F)
end = result.index('\x00')
print ''.join(result[:end])
https://www.netsarang.com/news/security_exploit_in_july_18_2017_build.html https://cdn.securelist.com/files/2017/08/ShadowPad_technical_description_PDF.pdf