剖析Cobalt Strike的DLL Stager

NVISO最近监测到了针对其金融部门客户的作为目的的行动。凭证员工关于可疑邮件的讲述,该攻击在其最先阶段就被发现。虽然没有造成任何危险,但我们通常会确定任何与之相关的指标,以确保对实行此行为者举行分外的监视。

被讲述邮件是申请公司其中一个公然招聘的职位,并试图发送恶意文档。除了行使现实的事情Offer,还引起我们注重的是恶意文档中还存在execution-guardrails 。通过对该文档剖析,发现了通过Component Object Model Hijacking(组件工具模子挟制)来维持Cobalt Strike Stager的意图

在我空闲的时间里,我很享受剖析NVISO符号的野外样本,因此进一步剖解 Cobalt Strike DLL payload,这篇博客文章将先容有用载荷的结构,设计选择,并重点先容若何削减日志足迹和缩短Shellcode的时间窗口。

剖析执行流

为了领会,恶意代码是若何事情的,我们必须去剖析其从最先到竣事的行为。在本节中,我们将先容一下游程。

1.通过DllMain初始化执行

2.通过WriteBufferToPipe,将加密shellcode发送到命名管道

3.通过管道举行读取,通过PipeDecryptExec,解密shellcode并执行

如前所述,恶意文档的DLL的载荷意图伪装为COM in-process server。有了这些熟悉,我们可以着眼与DLL公然的一些已知的入口点。

从手艺层面来说,恶意代码可以发生在该8个函数中随便一个,但恶意代码通常驻留在DllMain给定的函数中,除了TLS callbacks,它是最可能执行的函数。

DllMain:动态链接库(DLL)的可选入口点。当系统启动或终止历程或线程时,它将使用历程的第一个线程为每个已加载的DLL挪用入口点函数。当使用LoadLibraryFreeLibrary函数加载或卸载DLL时,系统也会挪用DLL的入口点函数。

docs.microsoft.com/zh-CN/windows/win32/dlls/dllmain

DllMain 入口点

从下面的捕捉效果可以看到,该DllMain函数只是通过确立一个新线程来简朴执行另一个函数。该线程函数我们命名为DllMainThread,它不需要提供任何参数即可执行。

剖析DllMainThread函数发现实在它是对我们将发现的恶意载荷的解密和执行函数的一个附加的包装.(被珍爱函数在捕捉中被称为DecryptBufferAndExec)

进一步深入,我们可以看到恶意逻辑的最先。具有Cobalt Strike履历的剖析师会立马意识到这个众所周知的MSSE-%d-server特征。

上面代码中发生了几件事:

1.该示例最先通过GetTickCount获取tick计数,然后将其除以0x26AA。只管获取的计数通常是时间的器量,但下一个操作仅仅将其作为随机数来使用

2.然后该示例继续挪用在sprintf函数周围的包装器。它的作用是花样化字符串为PipeName的缓冲区。如可以考察到,花样化字符串将是\\.\pipe\MSSE-%d-server其中%d为前面的除法盘算的效果。(例如:\\.\pipe\MSSE-1234-server)。这个管道花样是有据可查的Cobalt Strike 威胁指标。

3.通过在全局变量界说管道的名称,恶意代码将确立一个新线程以运行WriteBufferToPipeThread.此函数使我们接下来即将剖析的。

4.最后,在新的线程运行时,代码跳转到PipeDecryptExec例程。

到现在为止,我们有了线性的从DllMain入口点到DecryptBufferAndExec函数的执行历程,我们可以绘制如下游程:

如我们所见,两个线程现在将同时运行。让我们集中于其中写内容到管道 (WriteBufferToPipeThread) 的线程,其次是与之对应PipeDecryptExec的内容。

WriteBufferToPipe线程

写入天生的管道的线程是在DecryptBufferAndExec没有任何其他参数的情形下启动的。通过进入该函数,我们可以考察到它只是一个WriteBufferToPipe前的简朴的包装器,此外转达如下从全局Payload变量(。(由pPayload指针指向))恢复的参数。

1.shellcode的巨细,存储在offset=0x4处

2.指向包罗加密shellcode缓冲区的指针,该缓冲区存储在offset=0x14处

WriteBufferToPipe函数中,我们可以注重到代码是通过确立新管道最先的。管道的名称是从PipeName全局变量中恢复的,若是您还记得的话,该全局变量先前是由sprintf函数填充的。

代码确立了单个实例,通过挪用CreateNamedPipeA导出管道((PIPE_ACCESS_OUTBOUND)),然后通过挪用ConnectNamedPipe将其毗邻到该实例。

若是毗邻乐成,WriteBufferToPipe函数只要有shellcode的字节需要写入到管道就继续循环挪用WriteFile 来实现写入。

值得注重的一个主要细节是,一旦shellcoode写到了管道中,先前打开管道的句柄就通过CloseHandle来关闭。这解释管道的唯一目的就是为了传输加密的shellcode。

一旦`WriteBufferToPipe `函数执行完毕,线程终止。总体而言,执行流程异常简朴,可以如下绘制:

PipeDecryptExec 流程

作为快速恢复部门, 该PipeDecryptExec流程在确立WriteBufferToPipe线程后被立刻执行。执行的一个义务是分配一个内存区域,吸收要通过命名管道传输的shellcode。为此,将存储在全局Payload变量偏移0x4处的shellcode巨细作为参数来执行malloc挪用。

一旦缓冲区分配完成,代码将休眠1024毫秒(0x400),并将缓冲区位置和巨细作为参数挪用FillBufferFromPipe。若是该函数挪用失败则返回FALSE (0),那么代码会再次循环至该Sleep挪用,并再次实验操作,直到操作乐成为止。这些挪用和循环是必须的,由于多线程示例必须守候至shellcode写入到了管道中。

一旦将shellcode写入到了分配的缓冲区中,PipeDecryptExec最终会通过XorDecodeAndCreateThread来解密并执行shellcode。

要将加密的shellcode从管道传输到分配的缓冲区中, FillBufferFromPipe通过CreateFileA以只读的方式(GENERIC_READ))打开管道.就像确立管道一样,从全局PipeName变量中获取名称。若是接见管道失败,则函数将继续返回FALSE (0),而导致上述Sleep并重试的循环。

一旦管道以只读模式打开,FillBufferFromPipe函数接着会继续复制shellcode直到分配缓冲区使用ReadFile填满为止。缓冲区填满后,CloseHandle关闭命名管道的句柄,而且FillBufferFromPipe函数返回TRUE (1).

,

USDT交易平台

U交所(www.payusdt.vip),全球頂尖的USDT場外擔保交易平臺。

,

一旦FillBufferFromPipe乐成完成,命名管道已经完成了它的义务和加密的shellcode已经从一个存储区域移动到另一个。

回到挪用者PipeDecryptExec函数中,一旦FillBufferFromPipe函数挪用返回TRUEXorDecodeAndCreateThread将使用以下参数举行挪用:

  1. 包罗复制的shellcode的缓冲区。
  2. shellcode的长度,存储在全局Payload变量的offset=0x4处。
  3. 对称XOR解密密钥,存储在全局Payload变量的offset=0x8处。

挪用后,该XorDecodeAndCreateThread函数首先使用VirtualAlloc分配另一个内存区域。分配的区域具有读/写权限(PAGE_READWRITE),但不能执行。通过差异时具有可写和可执行权限,这个示例可能是实验逃避一些只寻找PAGE_EXECUTE_READWRITE区域的平安解决方案。

一旦这个区域被分配,函数就会在Shellcode缓冲区上循环并使用简朴的xor操作将每个字节解密

分配到新的内存区域中。

解密完成后,GetModuleHandleAndGetProcAddressToArg函数被挪用。它的作用放置指向两个有用的函数指针到内存:GetModuleHandleA and GetProcAddress。这些函数能够允许shellcode

剖析其他历程,而不必依赖于它们的导入。在存储这些指针之前,该GetModuleHandleAndGetProcAddressToArg函数首先确保特定值不是FALSE0)。令人惊讶的是,存储在全局变量(此处称为zero)中的该值始终为FALSE,因此指针从未被存储。

回到挪用者函数,XorDecodeAndCreateThread使用VirtualProtect更改shellcode的存储区域为可执行权限(PAGE_EXECUTE_READ),最终确立一个新线程。该线程从JumpToParameter函数最先,该函数充当shellcode的简朴包装,shellcode作为参数提供。

从这里最先,执行先前的加密Cobalt Strike shellcode stager ,剖析WinINet 历程,下载最终的信标然后执行它。我们不会在这篇文章中先容shellcode的剖析,由于它应该用一篇专属的文章来剖析。

只管最后一个流程包罗了更多的分支和逻辑,但总体流程图仍然异常简朴。

内存流剖析

在上述剖析中,最令人惊讶的是存在一个众所周知的命名管道。通过在管道出口处解密shellcode或举行历程间通讯,可以将管道用作防御逃避机制。

但在我们的案例中,它只是充当memcpy将加密的Shellcode从DLL移至另一个缓冲区的作用。

那么为什么要使用这种开销呢? 正如另以为同事指出,谜底在于Artifact Kit,这是Cobalt Strike的依赖项:

Cobalt Strike uses the Artifact Kit to generate its executables and DLLs. The Artifact Kit is a source code framework to build executables and DLLs that evade some anti-virus products. […] One of the techniques [see: src-common/bypass-pipe.c in the Artifact Kit] generates executables and DLLs that serve shellcode to themselves over a named pipe. If an anti-virus sandbox does not emulate named pipes, it will not find the known bad shellcode.

cobaltstrike.com/help-artifact-kit

正如我们在上图中所看到的,在malloc缓冲区加密shellcode的stageing为了逃避检测会发生大量开销。XorDecodeAndCreateThread直接从初始加密的Shellcode中读取则可以阻止这些操作。如下图所示,阻止使用命名管道将进一步消除对循环Sleep挪用的需求,由于数据将随时可用。

看来我们找到了一种削减获得shellcode时间的方式。然则盛行的防病毒解决方案是否被命名管道所诱骗?

修补执行流程

为了磨练该推测, 让我们改善恶意执行流程。对于初学者,我们可以跳过与管道毋庸的挪用,而直接在DllMainThread函数挪用PipeDecryptExec,从而绕过管道的确立和编写。汇编级的修补方式执行历程超出了本文的讨论局限,这里我们仅感兴趣于流程的抽象。

PipeDecryptExec功效还需要打补丁以跳过malloc分配\读取管道,并确保它能够提供XorDecodeAndCreateThread需要的DLL的加密shellcode而不是现在不存在的重复区域。

修补我们的执行流程后,若是平安解决方案将这些未使用的指令作为检测基础,则我们可以将其都清空。

应用补丁后, 们最终获得了一条线性且较短,直到执行Shellcode的路径。下图专注于此修补路径,不包罗下面的分支WriteBufferToPipeThread.

由于我们也弄清晰了shellcode若何举行加密(我们拥有xor密钥)一样,我们修改了两个示例以修改C2的地址,由于它可用于识别我们的目的客户。

为确保shellcode不依赖任何绕过的挪用,我们启动了一个快速的Python HTTPS服务器,并确保经由编辑的domain剖析为127.0.0.1。然后,我们可以通过rundll32.exe挪用原始DLL和修补的DLL,并考察shellcode若何去试图检索Cobalt Strike Beacon,以证实我们的补丁程序没有影响到shellcode。我们挪用的导出的StartW 函数是在Sleep挪用周围的简朴包装。

防病毒审查

那么,命名管道现实上是否可以用作防御逃避机制?只管有有用的方式来权衡补丁程序的影响(例如:对照多个沙盒解决方案),但VirusTotal确实提供了快速的开端评估。因此,我们向VirusTotal提交了以下版本重新编辑C2的样本:

  • wpdshext.dll.custom.vir 这是经由编辑的Cobalt Strike DLL。
  • wpdshext.dll.custom.patched.vir 这是我们未命名的补丁和编辑过的Cobalt Strike DLL。

由于原始的Cobalt Strike包罗可识其余特征(命名管道),因此我们希望修补后的版本具有较低的检测率,纵然Artifact Kit不这样以为。

正如我们预期的那样,Cobalt Strike所行使的命名管道开销现实上是作为检测基础。从以上截图中可以看出,原始版本(左)仅获得17次检测,而修补版本(右)获得的共16次检测少了一个。在给出的解决方案中,我们注重到ESET和Sophos未能检测到无管道版本,而ZoneAlarm无法识别原始版本。

一个值得注重的考察效果是,一个适配流程处于中央历程的补丁,但未对未使用的代码清0,效果发现它是检测到最多的版本,总共有20个匹配。泛起更高检测率是由于此修补程序允许不熟悉管道的防病毒提供商也仍然可以使用与管道相关的操作署名去定位shellcode。

只管这些测试针对的是默认的Cobalt Strike缺乏命名管道的行为。但有人可能会争辩,定制的命名管道模式将具有最好的效果。只管我们在最初的测试中没有想到这种变体,但我们在越日提交了一个版本(改变了管道名称为NVISO-RULES-%d而不是MSSE-%d-server),然后获得了18个检测效果。作为对照,我们另外两个样本的检测率在一夜之间增添到30+。然则,我们必须思量这18个检测效果受初始shellcode的影响。

结论

事实证实,逆向恶意的Cobalt Strike DLL比预期的要有趣。总体而言,我们注重到存在嘈杂的操作,这些操作的使用不是功效要求,甚至可以充当检测基础。为了证实我们的假设,我们修补了执行流程,并考察了简化版本若何以较低的检测率(险些未更改)到达C2服务器。

因此,这个剖析为什么很主要?

蓝队

首先,最主要的是,这个载荷的剖析突出显示了常见的Cobalt Strike DLL特征,使我们可以进一步微调检测规则。虽然这个Stager是第一个被剖析的DLL,但我们确实寻找了其他Cobalt Strike花样,好比默认信标和可以用的可延展的C2,包罗动态链接库和可移植的可执行文件。出乎意料的是,所有花样都共享了这个常见的文档纪录的MSSE-%d-server的管道名称,而且对开源检测规则的快速搜索显示了它被寻找的器械很少。

红队

除了对NVISO的防御行动有所辅助外,这项研究还使我们的进攻团队在选择使用定制交付机制方面感应加倍抚慰。更主要的是,遵照我们纪录的设计选择。在针对成熟环境的操作中使用命名管道更有可能引发危险信号,而且到现在为止,至少在不更改天生方式的情形下,似乎仍无法提供任何可逃避的优势。

对于下一个针对我们客户的介入者: 我期待着修改您的样本并测试更改后的管道名称的有用性。

译者注: 修改管道名称照样蛮有用的...

本文未翻译文章,原文链接:https://blog.nviso.eu/2021/04/26/anatomy-of-cobalt-strike-dll-stagers/

FiLecoin官网

FiLecoin官网(www.ipfs8.vip)是FiLecoin致力服务于使用FiLecoin存储和检索数据的官方权威平台。IPFS官网实时更新FiLecoin(FIL)行情、当前FiLecoin(FIL)矿池、FiLecoin(FIL)收益数据、各类FiLecoin(FIL)矿机出售信息。并开放FiLecoin(FIL)交易所、IPFS云矿机、IPFS矿机出售、租用、招商等业务。

Allbet Gaming声明:该文看法仅代表作者自己,与本平台无关。转载请注明:ipfs招商(www.ipfs8.vip):剖析Cobalt Strike的DLL Stager
发布评论

分享到:

Allbet Gaming

Allbet Gaming微信:8888

chia矿机(www.chia8.vip):无故隔离点算好?送你 100GB 数据
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。