Agent Code

How to design an agent for Apfell

This section covers how to design an Apfell agent to be properly modularized for custom payload creations.

Base Agent

The goal here is that the base agent doesn't care about the implementation of the C2 nor does it care about which commands are actually supported. The base agent should just try to look up and invoke a function based on the tasking it gets, and it should just leverage a basic set of C2 commands (independent of how they're actually implemented). This allows an arbitrary set of commands to be stamped into the agent at creation time and allows for the c2 implementation to be swapped out whenever without having to refactor any code.

So, the main components of base agent include:

  • A tasking loop at some interval with jitter

  • A lookup of commands to find the right function to call

  • Ideally a threaded architecture if the language supports it

    • This means a way to spin off tasks in different threads, track those threads, and potentially kill them if necessary

  • Uses c2 functions to return the results of command execution

You might be wondering how your base agent knows about which commands are loaded, which C2 implementation is being used, or how even get a payload's UUID. To help with this, Apfell does an initial level of "pre-processing" when creating an agent. To understand this, it's important to also understand how everything comes back together into a single agent since everything is split out by function.

Payload Creation

When creating an agent, you select a C2 profile, fill out the associated values, select a payload type, fill out the associated build transforms, select the commands you want in the initial payload, and click submit. At this point, Apfell makes a temporary directory and copies all of the agent code and c2 profile code to the new directory. Apfell then loops through all of the files to check for the pre-processor directives:

Additionally, for each file, Apfell checks the line for any C2 Profile Parameters and will replace them as needed. More about that in C2 Profile Code.

If Apfell doesn't find an instance of COMMANDS_HERE in any of the files, then Apfell assumes that the agent needs the commands to stay in their already defined folders and structures, so Apfell just copies them all into the same temp directory.

At this point, all of the code is stamped together, but more processing might need to happen. Apfell zips up the whole temp directory and, via RabbitMQ, sends it to the payload type's container. Inside the container, the code is unzipped to a temp directory, the creation transforms are executed, and the final payload is sent back via RabbitMQ to Apfell.

If you're curious about which file is the final one that Apfell treats as the payload, it's the contents returned by the final creation transform. So, it's up to the creation transforms to know which file is the final payload and return those bytes.

Commands

Commands should be self-containing - not relying on any other commands to be loaded. Commands should be able to expect access to C2 functions though.

Commands are able to send multiple responses back to Apfell throughout their execution by using the same task ID. This allows all of the responses to be appropriately tagged, tracked, and grouped together for the operator.

Last updated