Concepts#
Module Formats#
Every agent module has a format identifier — a string like "bof", "shellcode", "coff", "dll", or "tanto". Agent packages declare which formats they accept via supported_module_formats(). The teamserver matches modules to agents by checking that the module’s format appears in the agent’s supported list.
No format is privileged — compatibility is determined solely by matching identifiers. This means:
- A BOF module can be loaded by any agent that declares
"bof"support - A custom format
"crystal_palace"works if both agent and module agree on the identifier - Agents that don’t support module loading declare an empty list and are never offered modules
Managed Mode (default)#
When daemonize=false (the default), the loading agent manages the module’s lifecycle:
- The agent allocates memory, loads the payload, and executes it
- Results flow back through the loading agent’s communication channel
- Long-running modules (keylogger, proxy, port scanner) can stream results incrementally over multiple check-in cycles
- The operator can cleanly unload the module — the agent continues operating normally
- The teamserver tracks each managed module per agent: name, format, load time, status, originating task
Daemonized Mode#
When daemonize=true, the payload runs independently:
- The agent launches the payload in its own thread/process with full autonomy
- If the payload is itself an agent (contains crypto, codec, and registration logic), it establishes its own communication channel and registers independently as a new agent
- The teamserver records the parent-child relationship for topology tracking
- This is how agents deploy other agents through module loading — e.g., loading a Shinobi shellcode payload through another Shinobi as a daemonized module
Not all agents support daemonize. Agents that don’t MUST reject daemonized load requests with a clear error.
YAML Manifest#
Each agent module is a compiled payload accompanied by a YAML metadata manifest:
| |
Required Fields#
| Field | Type | Description |
|---|---|---|
name | str | Unique module name |
description | str | Human-readable description |
author | str | Module author |
format | str | Module format identifier (must match an agent’s supported_module_formats()) |
platforms | list[str] | Supported platforms (e.g., ["windows", "linux"]) |
architectures | list[str] | Supported architectures (e.g., ["x64", "x86", "arm64"]) |
Optional Fields#
| Field | Type | Description |
|---|---|---|
privilege_required | bool | Whether elevated privileges are needed (default: false) |
mitre_attack | list[str] | MITRE ATT&CK technique IDs |
options | dict | Options schema (same structure as server module options) |
Compatibility Filtering#
The teamserver filters agent modules on three dimensions before presenting them to an operator:
- Format — the module’s
formatmust appear in the agent’ssupported_module_formats() - Platform — the module’s
platformsmust include the agent’s platform - Architecture — the module’s
architecturesmust include the agent’s architecture
Only modules passing all three checks are offered. Attempting to load an incompatible module is rejected with a clear error.
Directory Structure#
Agent modules are organized by format and platform:
| |
Modules are discovered automatically from the agent_modules/ directory. Drop in a new module directory with its manifest and payload, then trigger a refresh.
Discovery and Refresh#
| |
Task Types#
load_module#
Delivers a module payload to an agent for execution.
| Parameter | Type | Description |
|---|---|---|
module_name | str | Name of the agent module to load |
daemonize | bool | false = managed mode (default), true = daemonized mode |
options | dict | Module-specific options from the manifest schema |
unload_module#
Terminates a running managed module and cleans up.
| Parameter | Type | Description |
|---|---|---|
module_name | str | Name of the loaded module to unload |
Tracking#
The teamserver tracks managed modules loaded in each agent:
| Field | Description |
|---|---|
| Module name | Which module is loaded |
| Format | The module’s format identifier |
| Load time | When the module was loaded |
| Status | running, completed, failed, unloaded |
| Originating task | The task that triggered the load |
For daemonized loads that result in a new agent, the teamserver records the parent_agent_id on the new agent for topology tracking.
Tanto Module Specification#
TantoC2 defines its own module format specification (working name: Tanto Module Spec, final name TBD) consisting of two parts:
- Module side: Binary format, entry point conventions, and how modules use the comms interface to send results and receive data.
- Loader side (Tanto Loader Spec): What agents must implement to load modules in this format — the comms interface, daemonize support, memory management (including zeroing on unload), and module lifecycle (load, run, unload, cleanup).
TantoC2-developed agents (e.g., Shinobi) MUST implement the Tanto Loader Spec. The specification is designed so that other C2 frameworks can adopt it independently.
A module development SDK with headers, build tooling, and example modules will be provided.
Next Steps#
- Agent Packages — how agents declare their capabilities
- Plugin Overview — the full plugin taxonomy
- Developer Guide: Agent Modules — step-by-step development guide