Files
Ares-mythic/APOLLO_AGENT_REFACTOR_ANALYSIS.md
2026-04-14 12:17:24 +07:00

10 KiB

Apollo agent refactor analysis

Scope

This review focuses on making the Apollo agent easier to maintain, safer to evolve, and easier to test. It does not include adding persistence, automatic privilege retention, exploit delivery, or integration with offensive exploit frameworks. If you need legitimate startup automation, the safer direction is a controlled startup pipeline for configuration validation, capability initialization, and operator-approved workflows.

Current architecture summary

Startup flow

  • Apollo/Program.cs performs COM security initialization, runs environmental keying checks, constructs Agent.Apollo, and immediately calls Start().
  • Apollo/Agent/Apollo.cs wires together the manager set in the constructor, dynamically builds C2 profile instances from Config, and enters a while (Alive) loop that repeatedly tries Checkin() until connected.
  • After a successful check-in, each connected egress profile is started directly from the agent.

Task execution flow

  • Apollo/Management/Tasks/TaskManager.cs loads the Tasks assembly with reflection.
  • Task types are keyed by class name and instantiated with Activator.CreateInstance.
  • ApolloInterop/Classes/Core/Tasking.cs wraps execution in an impersonation scope and provides task response helpers.
  • Responses, delegate messages, SOCKS datagrams, and port-forward datagrams are buffered in in-memory queues and flushed into outbound tasking messages.

Capability layout

  • Core responsibilities are split into managers for C2, peer routing, SOCKS, reverse port forwarding, files, identity, process, injection, and Kerberos ticket handling.
  • KerberosTickets/KerberosHelpers.cs contains low-level Windows interop, privilege elevation decisions, session enumeration, artifact collection, and ticket parsing in one helper surface.
  • Config.cs acts as both build-time template input and runtime configuration source through compile-time switches and placeholder replacement.

Main refactor opportunities

1. Introduce a bootstrap pipeline

The current startup path is very compact, but it tightly couples process entry, validation, agent construction, and network activation. A cleaner shape is:

  1. Program only handles process entry and fatal startup failures.
  2. AgentBootstrapper validates configuration and environment.
  3. AgentFactory builds the agent and managers.
  4. AgentLifecycle runs ordered startup stages.
  5. Apollo.Start() becomes a coordination loop instead of an all-in-one bootstrap location.

Recommended startup stages:

  • configuration validation
  • environmental keying validation
  • manager initialization
  • task registry initialization
  • optional warm-up checks
  • initial check-in
  • profile activation

This gives you a legitimate place to run automatic post-start actions such as:

  • preloading task modules
  • validating communication profiles
  • collecting local health telemetry
  • syncing sleep and jitter defaults
  • starting non-offensive local maintenance services

2. Replace reflection-heavy task discovery with a registry

TaskManager currently uses assembly-wide reflection and derives command names from t.FullName.Split('.')[1]. That works, but it is brittle.

Refactor target:

  • create a TaskDescriptor model with CommandName, Factory, and Metadata
  • build a TaskRegistry once at startup
  • validate duplicate command names early
  • separate built-in tasks from dynamically loaded modules
  • return explicit errors when a task exists but fails dependency or version checks

Benefits:

  • safer command lookup
  • easier unit testing
  • better diagnostics for load failures
  • simpler support for task versioning and capability flags

3. Add an explicit lifecycle model

The agent currently has Alive, Exit(), sleep primitives, and multiple background workers, but no unified lifecycle state.

Refactor target:

  • define states such as Created, Bootstrapping, Ready, Connecting, Connected, Stopping, Stopped
  • expose lifecycle transitions through a single coordinator
  • make workers consume a shared cancellation token instead of each area managing its own thread stop pattern
  • convert direct Thread.Sleep loops to wait handles or cancellable delays

Benefits:

  • cleaner shutdown
  • easier preview/test harnesses
  • fewer race conditions during reconnects and task cancellation

4. Split transport orchestration from agent identity

Apollo.Agent.Apollo currently owns agent metadata, manager wiring, transport setup, and connection behavior.

Refactor target:

  • keep Agent focused on state and capabilities
  • move profile construction into C2ProfileFactory
  • move connection policy into ConnectionOrchestrator
  • move check-in payload assembly into CheckinBuilder

That separation makes it easier to evolve reconnect logic, backoff, profile failover, and health reporting without growing the main agent class.

5. Replace silent catch blocks with structured error reporting

There are several areas where exceptions are swallowed or only partially surfaced. The biggest example is the check-in loop in Apollo/Agent/Apollo.cs.

Refactor target:

  • standardize a small error envelope with Source, Operation, Message, and Recoverable
  • log recoverable startup and connection failures to a bounded in-memory telemetry store
  • report actionable failures through the existing task response / debug plumbing where appropriate
  • keep operator-visible output separate from internal diagnostics

This improves operability without changing external behavior.

6. Untangle Config.cs

Config.cs currently mixes:

  • build-time templating
  • profile selection
  • placeholder values
  • local build defaults
  • runtime keying settings

Refactor target:

  • move profile parameter parsing to typed config records
  • isolate compile-time placeholders in a dedicated generated file
  • add a validator that checks required keys and malformed values before startup
  • expose normalized config objects to the rest of the codebase

This is one of the highest-value cleanups because it reduces hidden startup failures.

Kerberos-specific refactor suggestions

KerberosTickets/KerberosHelpers.cs is doing several jobs at once:

  • privilege escalation decision-making
  • impersonation
  • LSA interop
  • session enumeration
  • ticket cache parsing
  • artifact creation
  • error handling

Recommended split:

KerberosSessionProvider

  • enumerate logon sessions
  • resolve logon session metadata
  • own LSA handle lifetime

KerberosTicketQueryService

  • resolve auth package
  • issue cache queries
  • translate native structures into DTOs

KerberosPrivilegeContext

  • decide when elevation is required
  • acquire and dispose impersonation handles safely
  • expose a single execution wrapper

KerberosArtifactRecorder

  • isolate artifact generation from query logic
  • remove shared mutable static collections

Additional improvements:

  • replace static mutable state like systemHandle and createdArtifacts with request-scoped objects
  • make every native handle owner disposable
  • standardize native call result handling instead of mixing return checks and Marshal.GetLastWin32Error()
  • return typed result objects rather than partial tuples where possible

Safer startup automation

If your goal is "do more automatically after running", the safe refactor is not persistence. It is a startup jobs pipeline with explicit policy.

Recommended design:

IStartupAction

Each startup action implements:

  • Name
  • Order
  • ShouldRun(AgentContext context)
  • ExecuteAsync(AgentContext context, CancellationToken token)

Examples of legitimate startup actions:

  • validate config
  • warm up interop resolvers
  • register built-in capabilities
  • start transport health probes
  • preload task metadata
  • publish a local readiness snapshot

StartupActionRunner

  • resolves ordered startup actions
  • runs them once during bootstrap
  • records result status
  • stops startup on hard failures

StartupPolicy

  • defines which startup actions are enabled for a build or environment
  • prevents hidden behavior from appearing in release builds

This gives you automation without embedding covert or unauthorized persistence behavior.

What not to merge into the agent

I do not recommend turning this agent into something that automatically establishes persistence, silently escalates access, or consumes exploit tooling such as BlueHammer-style zero-day workflows. That crosses from maintainability work into building or improving offensive malware behavior.

If you are evaluating external research for a legitimate lab or detection-validation use case, keep it isolated outside the agent core:

  • use a separate lab-only harness
  • disable it by default at build time
  • require explicit operator input
  • keep it out of the bootstrap path
  • never couple it to automatic startup behavior

Phase 1

  • add AgentBootstrapper
  • add typed config validation
  • move check-in message construction into a dedicated builder
  • remove silent exception swallowing in startup and connection logic

Phase 2

  • introduce TaskRegistry
  • replace ad hoc reflection lookup with validated registration
  • add lifecycle states and shared cancellation control

Phase 3

  • split KerberosHelpers into smaller services
  • remove static mutable helper state
  • add native handle ownership wrappers and typed result objects

Phase 4

  • add IStartupAction and StartupActionRunner
  • restrict startup automation to explicit, policy-controlled, non-persistence behaviors

Concrete hotspots to address first

  • Apollo/Program.cs: entry point currently owns validation and direct startup
  • Apollo/Agent/Apollo.cs: constructor does too much wiring and Start() mixes connection policy with runtime looping
  • Apollo/Management/Tasks/TaskManager.cs: task discovery and worker orchestration are tightly coupled
  • ApolloInterop/Classes/Core/Tasking.cs: good reusable base, but it would benefit from richer execution metadata and cancellation reporting
  • KerberosTickets/KerberosHelpers.cs: too many responsibilities in one helper and too much static mutable state
  • Apollo/Config.cs: compile-time templating and runtime config concerns are mixed together

Suggested first refactor slice

The smallest high-value first step is:

  1. create AgentBootstrapper
  2. move environment checks out of Program
  3. extract CheckinBuilder
  4. add a startup action pipeline for safe initialization only
  5. add a TaskRegistry abstraction in front of the current TaskManager

That sequence improves structure immediately without changing the external command surface.