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.csperforms COM security initialization, runs environmental keying checks, constructsAgent.Apollo, and immediately callsStart().Apollo/Agent/Apollo.cswires together the manager set in the constructor, dynamically builds C2 profile instances fromConfig, and enters awhile (Alive)loop that repeatedly triesCheckin()until connected.- After a successful check-in, each connected egress profile is started directly from the agent.
Task execution flow
Apollo/Management/Tasks/TaskManager.csloads theTasksassembly with reflection.- Task types are keyed by class name and instantiated with
Activator.CreateInstance. ApolloInterop/Classes/Core/Tasking.cswraps 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.cscontains low-level Windows interop, privilege elevation decisions, session enumeration, artifact collection, and ticket parsing in one helper surface.Config.csacts 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:
Programonly handles process entry and fatal startup failures.AgentBootstrappervalidates configuration and environment.AgentFactorybuilds the agent and managers.AgentLifecycleruns ordered startup stages.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
TaskDescriptormodel withCommandName,Factory, andMetadata - build a
TaskRegistryonce 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.Sleeploops 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
Agentfocused 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, andRecoverable - 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
systemHandleandcreatedArtifactswith 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:
NameOrderShouldRun(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
Recommended implementation order
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
KerberosHelpersinto smaller services - remove static mutable helper state
- add native handle ownership wrappers and typed result objects
Phase 4
- add
IStartupActionandStartupActionRunner - 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 startupApollo/Agent/Apollo.cs: constructor does too much wiring andStart()mixes connection policy with runtime loopingApollo/Management/Tasks/TaskManager.cs: task discovery and worker orchestration are tightly coupledApolloInterop/Classes/Core/Tasking.cs: good reusable base, but it would benefit from richer execution metadata and cancellation reportingKerberosTickets/KerberosHelpers.cs: too many responsibilities in one helper and too much static mutable stateApollo/Config.cs: compile-time templating and runtime config concerns are mixed together
Suggested first refactor slice
The smallest high-value first step is:
- create
AgentBootstrapper - move environment checks out of
Program - extract
CheckinBuilder - add a startup action pipeline for safe initialization only
- add a
TaskRegistryabstraction in front of the currentTaskManager
That sequence improves structure immediately without changing the external command surface.