本文为elastic security团队成员Gabriel Landau的《What you need to know about Process Ghosting, a new executable image tampering attack》文章中文翻译,因本人水平有限如发现相关错误欢迎指正。
基于Windows环境的防御企业安全团队通常依赖于反病毒产品作为抵御恶意可执行文件的第一道防线。微软为安全产品供应商提供了注册系统回调的功能,这些回调将在系统创建进程的时候被调用。相关安全产品的驱动程序开发人员可以调用诸如PsSetCreateProcessNotifyRoutineEx之类的API来感知系统创建进程的事件。
尽管如此,PsSetCreateProcessNotifyRoutineEx设置的回调函数在创建进程那一瞬时实际上并不会被调用,而是在该进程中创建第一个线程之后才会被回调所感知到。这就在创建进程和系统通知安全产品创建进程的事件之间产生了时间差。它这就给恶意软件的制作者提供了一个攻击窗口,可以在安全产品扫描磁盘中的可执行文件之前篡改它。近些年来这种篡改磁盘PE文件攻击的例子包括Process Doppelgänging和Process Herpaderping,它们使用这种技术来逃避安全产品的静态文件扫描。
本文描述了一种新的可执行映像篡改攻击,类似于Doppelgänging和Herpaderping,但与他们又有一些不同的地方。利用这种新技术,攻击者可以将恶意软件写入磁盘并使其难以扫描或删除,在可执行文件加载之后再执行删除命令,将恶意程序伪装成就像磁盘上的一个普通文件一样。这种技术不涉及代码注入、傀儡进程或事务性NTFS (TxF)。
进程的诞生(The birth of a process)
Windows任务管理器会显示系统上运行的进程列表,这些进程的每一个都与磁盘上的一个可执行文件相对应,例如svchost.exe。这是因为Windows的进程是从磁盘中的可执行文件启动,通常可执行文件以EXE文件扩展名结尾。
重要的是要注意进程不是可执行文件,可执行文件也不是进程。在上面任务管理器的示例中,从RuntimeBroker.exe和svchost.exe就启动了多个进程,可执行文件和进程是一个一对多的关系。
要启动一个新进程,必须执行一系列步骤。在现代的Windows中,进程创建通常由NtCreateUserProcess在内核中执行,然而某些个别组件API如NtCreateProcessEx等,仍因为向后兼容的目的还在公开使用。这些步骤是:
- 打开可执行文件的句柄。 Example: hFile = CreateFile(“C:\Windows\System32\svchost.exe”)
- 为文件创建一个映像节区(image sections)。节区(section )将文件或文件的一部分映射到内存中。映像节区是一种特殊类型的节,对应于可移植可执行文件(PE),并且只能从PE (EXE, DLL等)文件中创建。Example: hSection = NtCreateSection(hFile, SEC_IMAGE)
- 使用映像节区(image sections)创建一个进程。 Example: hProcess = NtCreateProcessEx(hSection)
- 分配进程参数和环境变量。Example: CreateEnvironmentBlock/NtWriteVirtualMemory
- 创建一个在进程中执行的主线程。 Example: NtCreateThreadEx
下面是它在Process Monitor中的样子:
如Process Monitor中所示,explorer.exe启动notepad.exe的过程
进程是从可执行文件启动的,但是可执行文件中的一些数据在映射到进程时可能被修改。为了考虑这些修改,Windows内存管理器在创建映像节区时会再内存中缓存对应的映像节区。这就意味着映像节区(image sections)可能偏离其可执行文件。
扫描恶意软件进程(Scanning processes for malware)
微软为安全产品供应商提供了注册回调的功能,这些回调将在系统上创建进程和线程时被调用。驱动程序开发人员可以调用诸如PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutineEx等API来接收此类事件。
尽管如此,PsSetCreateProcessNotifyRoutineEx回调实际上不是在进程创建时调用的,而是在这些进程中创建第一个线程时调用的。这就在创建进程和通知安全产品创建进程之间产生了时间差。这就给恶意软件的制作者提供了一个攻击窗口,可以在安全产品扫描磁盘中的可执行文件之前篡改它。
请注意,未文档化的进程创建API NtCreateProcess使用的是节区(Section)句柄,而不是文件句柄:
NTSYSCALLAPI
NTSTATUS
NTAPI
NtCreateProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ HANDLE ParentProcess,
_In_ BOOLEAN InheritObjectTable,
_In_opt_ HANDLE SectionHandle,
_In_opt_ HANDLE DebugPort,
_In_opt_ HANDLE ExceptionPort
);
当一个进程启动时,安全产品将通过系统回调获得关于正在启动的进程的以下信息:
typedef struct _PS_CREATE_NOTIFY_INFO {
SIZE_T Size;
union {
ULONG Flags;
struct {
ULONG FileOpenNameAvailable : 1;
ULONG IsSubsystemProcess : 1;
ULONG Reserved : 30;
};
};
HANDLE ParentProcessId;
CLIENT_ID CreatingThreadId;
struct _FILE_OBJECT *FileObject;
PCUNICODE_STRING ImageFileName;
PCUNICODE_STRING CommandLine;
NTSTATUS CreationStatus;
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;
值得注意的是FILE_OBJECT,它是与上一节中传递给NtCreateSection的HANDLE对应的文件内核对象。这个FILE_OBJECT通常对应磁盘上的一个文件,可以安全产品用来被扫描恶意软件。
安全产品还可能使用文件系统minifilter 回调,当文件被创建、修改或关闭时,它会接收通知。扫描每个读和写操作对系统的性能影响可能很大,因此出于性能考虑,文件通常在打开和关闭时进行扫描。
还有其他潜在的安全产品拦截点,我们将不在这里讨论。更多信息请看这篇演讲
前人工作(Prior work)
Windows事务性NTFS (TxF)是一种允许应用程序作为单个原子事务执行一系列文件系统操作,然后提交或回滚该事务的机制。事务中可能存在文件回滚,则底层的文件系统永远不会看到这些文件更改。使用TxF,可以从在事务中的文件创建映像节区,然后回滚该事务。可以从这样修改后的映像节区创建一个进程,而磁盘中的文件还是原来的内容。
在创建映像节区之后,Process Herpaderping利用一个诱饵PE文件使用现有的文件句柄覆盖其可执行文件。将诱饵PE文件留在磁盘上,但它与运行在内存中的映像节区不同。在有payload进程的整个生命周期中,诱饵PE文件都保留在磁盘上。
推荐一篇分析该项技术很好的文章以便读者更好理解 bbs.pediy.com/thread-271554.htm (成哥yyds
进程重映像利用了Windows内核中的缓存同步问题,它会导致可执行文件的路径与从该可执行文件创建的映像节区所报告的路径不匹配。通过在一个诱饵路径上加载DLL,然后卸载它,然后从一个新路径加载它,许多Windows API将返回旧路径。这可能可以欺骗安全产品,使其在错误的路径上查找加载的映像。
进程重影(Ghosting a process)
我们可以在Doppelgänging和Herpaderping的基础上运行已删除的可执行文件。在Windows上有几种删除文件的方法,包括:
- 在设置FILE_SUPERSEDE或CREATE_ALWAYS标志的在旧文件之上创建一个新文件。
- 在创建或打开文件时设置 FILE_DELETE_ON_CLOSE或FILE_FLAG_DELETE_ON_CLOSE标志。
- 通过 NtSetInformationFile设置FileDispositionInformation时,将 FILE_DISPOSITION_INFORMATION结构中的 DeleteFile 字段设置为 TRUE。
Windows会试图阻止修改映射后的可执行文件,一旦一个文件被映射到一个节区(section)部分,尝试用FILE_WRITE_DATA参数打开它(修改它),将会报ERROR_SHARING_VIOLATION错误。使用FILE_DELETE_ON_CLOSE/FILE_FLAG_DELETE_ON_CLOSE参数尝试删除操作,会报ERROR_SHARING_VIOLATION错误。
NtSetInformationFile(FileDispositionInformation)函数调用时需要有DELETE访问权限的文件。即使将DELETE访问权限授予映射到映像节区的文件,设置NtSetInformationFile(FileDispositionInformation)时也会出现STATUS_CANNOT_DELETE错误。尝试删除设置了 FILE_SUPERCEDE/CREATE_ALWAYS 参数的文件会导致ACCESS_DENIED错误。(ps: 按照译者理解在NtSetInformationFile应该表达的是在设置FileDispositionInformation的时候不能是映射后的文件,得是默认状态下的磁盘文件否则会出现STATUS_CANNOT_DELETE错误)
然而需要注意的是,这个删除限制只有在可执行文件映射到映像节区时才生效。这意味着可以创建一个文件,将其标记为删除,将其映射到一个映像节区,关闭文件句柄以完成删除,然后从现在的无文件节区创建一个进程。这就是进程重影(Process Ghosting)。
简单来说就是一种通过自删除的手法进行静态文件绕过的解决方案
攻击流程为:
- 创建一个临时文件
- 调用NtSetInformationFile(FileDispositionInformation)将文件设置为删除挂起(delete-pending )状态。注意:尝试使用FILE_DELETE_ON_CLOSE将不会直接删除文件。
- 将payload内容写入刚才创建的临时文件,因为文件已经处于删除挂起状态,所以内容不会被一直保存,删除挂起状态还会阻止外部进程尝试打开文件。
- 为该文件创建一个映像节区。
- 关闭删除挂起状态的文件句柄,随后系统会删除该临时文件。
- 使用第4步的映像节区创建一个进程。
- 设置进程参数和环境变量。
- 创建主线程
反病毒程序的回调在线程创建时被调用,线程创建时其对应的文件已经被删除了。对于已经被删除的文件做打开或者I/O执行会产生STATUS_FILE_DELETED的报错。如果尝试打开处于删除挂起状态的文件则会报STATUS_DELETE_PENDING错误。
这种类型的篡改也可以应用于DLL,因为DLL也是映射到映像节区的。
样例(Demo)
本节因为特殊原因与原文有些省略,不过代码都是使用这个仓库读者可以参考源码进行理解
通过观察Process Monitor的记录可以发现MsMpEng.exe(Windows Defender中的一个后台进程)想要打开payload文件时会出现STATUS_FILE_DELETED/STATUS_DELETE_PENDING的报错,进而绕过反病毒程序的静态文件扫描。
检测(Detection)
Elastic Security团队已经可以检测诸如Doppelgänging, Herpaderping和Ghosting这类的进程映像篡改攻击,检测的原理大致为在进程创建的回调期间检验FILE_OBJECT 结构是否有异常。
技术比较(Comparing techniques)
基于Process Herpaderping文档中的一个有用的表格,我们可以比较各种技术之间的API调用流程:
总结(Conclusion)
在这篇博客中,我们研究了Windows可执行映像篡改攻击的现状,然后披露了一种新的类似攻击。然后,我们演示了这种绕过常见安全软件的攻击,并演示了如何使用Elastic Security检测它。若要在您的系统环境中发现进程篡改等威胁,请在Elastic Cloud上安装最新版本的Elastic Security,并确保学习了我们的快速入门培训,为成功实验做好准备。Happy hunting!
漏洞披露责任:我们在2021-05-06向MSRC提交了一份漏洞报告,包括这篇博客文章的草稿、演示视频和PoC的源代码。他们在2021-05-10认为这不算是一个漏洞。
以下内容为译者学习后的总结及参考文献
仓库代码流程
源码参考的是https://github.com/hasherezade/process_ghosting,其大致流程如下:
参考链接
- https://www.elastic.co/cn/blog/process-ghosting-a-new-executable-image-tampering-attack
- https://bbs.pediy.com/thread-271554.htm#msg_header_h2_5
- https://mp.weixin.qq.com/s/HE0Re6RZ0wojTwPnHjeF3Q
- https://www.hackingarticles.in/process-ghosting-attack/
- https://github.com/hasherezade/process_ghosting
- https://github.com/knightswd/ProcessGhosting