作者:bird@TSRC
近日,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