概述
2023年十月,奇安信威胁情报中心发布了《Operation HideBear:俄语威胁者将目标瞄准东亚和北美 [1]一文,我们在文中提到攻击者的目标有着经济和技术的双重目的,经济上瞄准投资机构和比特币公司(个人),技术上对我国电感元器件制造商和生物抗体研究制药有着浓厚的兴趣,我们以中等程度的信心将其归属于Strom-0978,并于2023年末对其进行持续的跟踪中捕获了一个非常奇怪的样本,经过长时间的逆向分析发现攻击者使用了一套之前从未见披露过的内核注入技术,我们将其命名为“Step Bear”,在注入中使用天堂之门和地狱之门的调用方式启动一些不常见的内核函数导致该注入技术能够绕过主流的EDR检测,注入流程如下:
Step1:恶意进程调用内核函数xxxSetClassLong向内核中的tagCLS额外类内存写入精心构造的Shellcode。shellcode包含一段gadgets工具函数和多段小型shellcode组成。
Step2:内核自动将tagCLS额外类内存映射到notepad的只读内存块中实现代码注入。
Step3:使用内核版的wordwrap方式触发RPC调用链执行notepad只读内存块中的shellcode,RPC的调用链会先触发shellcode中的gadgets函数,然后调用VirtualProtect将小型shellcode的属性改为RWX,并执行。
Step4:相同的方式将精心构造的shellcode写入到tagCLS额外类内存中。
Step5:内核将shellcode映射到浏览器进程的只读内存块中实现代码注入。
Step6:攻击者通过向windows中一个未公开的com窗口发送自定义的消息后触发浏览器中只读shellcode的RPC调用链。
Step7:分配RWX类型的内存将CRX插件注入到浏览器中。
注入技术介绍
注入原理
攻击者在注入过程使用地狱之门(Hell's Gate)的syscall调用NtUserSetClassLong,该函数底层会调用win32kfull.sys中的xxxSetClassLong函数。
NtUserSetClassLong,第一个参数是刚刚创建的窗口句柄,第二个参数为偏移,一开始赋值为0,第三个参数为指定的数据,xxxSetClassLong函数结构如下, 该函数主要功能是将指定数据写入内核对象tagCLS的额外类内存中:
在我们双机调试的内核版本中tagCLS结构大小为0xA0。
这段内核内存有一个重要的属性:写入数据的这段内核空间在同一用户会话下的任意一个应用层进程中都有内存映射,如果我们此时随便打开一个进程,例如计算器,那么会在calc进程中的某个映射内存块中找个上述写入的指定数据,
映射到应用层进程中的只读内存块大小和内容都在变化,但基址不变,换句话说攻击者只要操纵malicious.exe修改这段共享的内核空间数据,就可以实现针对任意进程的代码注入,但是注入的内存块属性为只读,所以攻击者接下来需要使用某种方式来触发共享内存块中的恶意代码。
触发前的准备
样本首先会执行一些初始化操作,包括加载系统dll、获取自身模块名、判断自身权限等。
之后会创建notepad进程,获取notepad进程中edit窗口的句柄。
注册一个带有额外类内存的窗口类,其窗口过程为默认的NtdllDefWindowProc,额外类内存大小为0x16010。
之后会使用这个窗口类创建一个仅消息窗口,创建后会调用NtUserSetClassLong填充内核中共享内存块中的数据。
此次填充的数据仅用于定位内核共享数据块映射到malicious.exe中的具体地址,样本通过查找TEB->Win32ClientInfo->phkCurrent结构下的AllocationBase来确定窗口内存的基址,接着对该区域的内存与之前调用填充的数据进行逐个比较找到共享内存块的地址,找到位置后再次调用NtUserSetClassLong将填充数据清空。
在notepad内存中搜索指定API地址和共享内存块的地址,用于之后填充的shellcode调用。
其搜索的API如下
API解析完毕后会在内存中解密一段payload,我们将其称为shellcodeA。
然后使用天堂之门(Heaven's Gate)的调用方式启动NtUserSetClassLongPtr向内核共享数据注入shellcodeA。
ShellcodeA的结构非常复杂,shellcodeA中包含:gadgets工具函数和多段小型shellcode组成,我们将其统称为shellcodeB,具体结构如下:
至此ShellCodeA已经注入到notepad进程中,下面开始进入触发流程。
怎么触发?
由于攻击者实现了两次注入,两次注入的方式均是通过内核共享内存块来实现的,但是两次的触发方式则完全不同,Notepad的触发方式较为常见,而Chrome中的触发方式之前从未见到。
Notepad触发
攻击者通过通过设置EM_SETWORDBREAKPROC来注册文本包装器回调函数,并发送WM_LBUTTONDBLCLK消息来触发回调函数,回调函数的参数地址则在之前通过WM_SETTEXT的消息进行了设置。
最终触发的函数为为rpcrt4.dll中的I_RpcFreePipeBuffer。
I_RpcFreePipeBuffer执行后会直接调用Message->Handle + 0x80位置处的函数
根据ShellcodeA中的结构此时Message->Handle + 0x80的位置为NdrServerCallAll的地址。
NdrServerCallAll继承了RpcFreePipeBuffer的Message参数,在进行简单处理后会调用Ndr64StubWorker
PRPC_MESSAGE相关的结构如下(x86)
Ndr64StubWorker首先通过Ndr64pServerUnMarshal函数解析Message参数并将解析后的数据传递到栈中指向的内存里。
PRPC_MESSAGE_A是NdrServerCallAll的参数,会调用Invoke函数执行PRPC_MESSAGE_A中DispatchTable的地址,
PRPC_MESSAGE_A中DispatchTable的地址还是NdrServerCallAll,至此形成套娃,会第二次进入NdrServerCallAll,此时的参数为PRPC_MESSAGE_B,而PRPC_MESSAGE_B的DispatchTable函数为VirtualProtect,将共享内存块中shellcodeB的内存属性改为可读可写可执行。
调用链如下:
攻击者使用了Ndr64StubWorker中一个从未披露的函数来执行ShellcodeB,在调用Invoke函数后会顺序执行到Ndr64pFreeParams,这个函数可以执行攻击者控制的函数地址。
由于ShellcodeA的特殊结构导致目前函数调用进入NdrServerCallAll两层嵌套的状态,所以会在两次Invoke函数和两次Ndr64pFreeParams处执行攻击者控制的代码。执行顺序分别为Invoke_A->Invoke_B->Ndr64pFreeParams_B-> Ndr64pFreeParams_A,那么接下来会执行Ndr64pFreeParams_B中攻击者控制的代码,此时要调用的函数被攻击者设计成CallWindowProcW,第一个参数lpPrevWndFunc被填充为ShellcodeB的起始地址。
而0x3C0大小的ShellcodeB实际上有三段小型shellcode组成,我们将其分为ShellcodeC、shellcodeD和shellcodeE,ShellcodeB在执行链中的结构如下:
ShellcodeC中的代码主要为Jmp,跳转到ShellcodeD
ShellcodeD调用RtlAddVectoredExceptionHandler注册异常处理函数,当执行流执行到Ndr64pFreeParams_B之后,Ndr64pFreeParams_A之前时,必然会触发SEH,
异常回调函数逻辑如下:
调用SendMessageTimeoutW函数将notepad的edit窗口过程修改为RpcAsyncRegisterInfo,第二个SendMessageTimeoutW会向malicious.exe进程创建的窗口进行通信,窗口接收到消息后会将ShellCodeA的起始结构(Message->Handle + 0x80)的位置写入ShellCodeE的地址,ShellCodeE是继shellcodeA之后第二阶段的Shellcode。
最终会执行到Ndr64pFreeParams_A,攻击者设计调用的函数为I_RpcFreePipeBuffer,SEH回调函数中已经将(Message->Handle + 0x80)指向的地址修改为ShellcodeE的起始地址,所以接下来会正式进入第二阶段的逻辑,ShellcodeE调用情况如下:
ShellcodeE首先会申请可执行的内存。
第一次与malicious.exe通信,主要用于ShellcodeF中地址的重定位。
第二次与malicious.exe通信,通知malicious.exe将ShellcodeF注入到共享内存块中。
注入完成后将ShellcodeF拷贝到刚才分配可执行内存块中,至此一次注入循环结束,注入流程图如下:
Chrome触发
ShellcodeF的主要功能是从Notepad进程将恶意代码注入到Chrome进程中,开启第二轮注入流程,通过Chrome_MessageWindow寻找浏览器进程。
在注入前会对当前时间进行验证,验证当前时间是否小于64AE6B71(2023-07-12 08:59:29),如果小于则进入注入流程。
这意味着具体的攻击活动发生在2023-07-12之前,使用相同的注入方式将shellcode写入共享内存块后,攻击者在浏览器进程中寻找一个未公开的com隐藏窗口。
调用NtUserMessageCall向未公开的com窗口类OleMainThreadWndClass发送指定的消息。
第一个参数为未公开隐藏窗口的句柄、第二个参数为MSG消息,我们推测是该未知窗口的自定义消息0x405、第三个参数数据未知、第四个参数是一个结构体的指针,指针中包含了目标浏览器进程中共享内存块的地址。我们通过调试发现浏览器进程中的MsgWaitForMultipleObjectsEx 来等待消息事件信号。
该函数检测到事件信号并返回,随后进入消息分发过程 PeekMessageW,
PeekMessageW 会调用windows 底层的消息分发过程:NtUserPeekMessage
在进入 NtUserPeekMessage(R0) 后,根据获取的消息进入用户回调阶段 KiUserCallbackDispatcher(R3),经过如下调用链 KiUserCallbackDispatcher -> __fnDWORD -> DispatchClientMessage -> UserCallWinProcCheckWow,此时UserCallWinProcCheckWow的参数如下:
其中第二个参数被设置为 ThreadWndProc,该函数用于进入接收消息窗口句柄的窗口回调,UserCallWinProcCheckWow 会调用此函数,进入 ThreadWndProc 之后会根据消息值进行分发,其中 0x405 会进入 OleMainThreadWndProc 函数,根据函数名推测它是 OleMainThreadWndClass 窗口类注册的默认回调函数。
在 OleMainThreadWndProc 中在经过一次判断,进入 GetSingleThreadedHost,
此时 GetSingleThreadedHost 的参数 lParam 为攻击者设置的地址,该函数在开头处会调用 [[lParam] + 0x18] 处的地址。
该地址被攻击者设置为 NdrServerCallAll,从而进入与之前 notepad 注入相同的执行流程,调用栈如下:
与Notepad执行流程的区别在于,notepad是通过I_RpcFreePipeBuffer函数进入到NdrServerCallAll,而浏览器进程中则是通过combase库中的GetSingleThreadedHost进入NdrServerCallAll进而触发浏览器共享内存块中的RPC链,创建RWX属性的内存块,最终会在浏览器内存中加载一个CRX插件。
CRX插件的主要功能窃取浏览器中的数据,存在一个规则文件用于替换浏览器显示的信息,例如与加密货币相关的操作时,将提款请求替换为授权请求。
在JS代码中发现了俄文的注释信息。
最后将窃取的数据回传到C2服务器上。
影响
目前主流的APT组织都可以称为“Loader生产者”,花重金从外包公司购买一些劣质的加载器加载通用的木马后门,随即开始攻击活动,从技术角度来看这种类型的攻击并不具备逆向分析的意义,即使把“Step Bear”放到整个恶意软件的发展史来作比较,使用如此复杂的内核注入技术运行恶意代码的在野攻击案例并不多见,其中有些技术常见于windows内核提权的EXP代码中 [2],当然“Step Bear”注入技术中用了一些从未披露过的技术,例如Ndr64pFreeParams函数劫持执行流、OleMainThreadWndClass隐藏窗口的自定义消息0x405的触发点,这意味着一个顶级的windows内核研究员利用自己对windows内核的独特理解设计了一套顶级的注入框架,结果对主流的EDR产品实现了降维打击,该人员对恶意代码的设计和编写也非常的熟练,实现注入的逻辑和触发的逻辑已经框架化,可以复用到任何攻击场景中。
从shellcode的时间验证和CRX打包的时间可以推断Storm-0978开展攻击时间位于2023年3月-7月,在这个时间线下该团伙正在使用CVE-2023-36884对西方国家进行攻击活动,尽管我们没有捕获到攻击入口,但是我们推测应该与之前的活动类似,使用高仿下载页面进行钓鱼活动,奇安信威胁情报中心未来会对“Operation HideBear”行动保持持续的监控。
总结
目前,基于奇安信威胁情报中心的威胁情报数据的全线产品,包括奇安信威胁情报平台(TIP)、天擎、天眼高级威胁检测系统、奇安信NGSOC、奇安信态势感知等,都已经支持对此类攻击的精确检测。
IOC
有关该组织的详细IOC请联系奇安信威胁情报中心(ti.qianxin.com)
参考链接
[1]. https://mp.weixin.qq.com/s/bXvgdEevOmuMGOKwTisqXg
[2]. https://hackyboiz.github.io/2023/10/30/pwndorei/newjeans-hyper-v-pt5/