Dark-Agent
Dark Agent
A production-ready Linux/macOS C2 agent written in Crystal for Mythic framework. Features static OpenSSL linking, comprehensive BOF support, and extensive built-in Unix commands.
Overview
Dark Agent is a fully-featured Mythic C2 Agent for Linux and macOS environments. Built in Crystal with statically-linked OpenSSL, it provides comprehensive post-exploitation capabilities through COFF/BOF loading, extensive system commands, SOCKS proxy support, and flexible communication profiles.
Platform Support
| Build | OS | Min glibc | Coverage |
|---|---|---|---|
| Dynamic | Linux x86_64 | 2.27 | RHEL 8+, Ubuntu 18.04+, Debian 10+ |
| Dynamic | macOS arm64 | N/A | macOS 12+ (Apple Silicon) |
Linux builds have OpenSSL statically linked with no libssl dependency on the target. macOS binaries are cross-compiled on Linux via Zig and ad-hoc signed with rcodesign (SHA-256 CodeDirectory hashes required by macOS 14+).
Mythic Configuration
Agent Configuration
- Agent Name: dark
- Supported OS: Linux, macOS
- File Extension: bin
- C2 Profiles: HTTP, HTTPX (malleable)
Build Parameters:
debug_mode: Boolean (default: false) - Enable debug logging and verbose outputdebug_socks: Boolean (default: false) - Enable SOCKS proxy debug loggingencryption: Boolean (default: true) - Enable AES-256-CBC encryption for C2 trafficdisable_ssl_verify: Boolean (default: false) - Skip SSL certificate verificationsymmetric_jitter: Boolean (default: false) - Use symmetric jitter (sleep_time ± jitter%) for better OPSECrealtime: Boolean (default: false) - Immediately send command responses without waiting for sleep intervalchunk_size: Number (default: 512) - Size of file transfer chunks in KB, affects upload/download performance
Available Commands: Extensive built-in commands including system utilities, file operations, network tools, and agent management
OPSEC Checks: Dark Agent includes built-in operational security checks for high-risk commands:
shell- All shell commands require approval from another operatorsleep- Sleep intervals < 10 seconds require operator approval (special warning for sleep 0)kill- All process termination requires operator approval
C2 Profile Types
HTTP Profile
The standard HTTP profile is a simpler implementation that uses regular HTTP requests. It's easier to configure but offers less customization options.
HTTPX Profile (Malleable C2)
The HTTPX profile is a more advanced implementation that supports malleable C2 profiles with extensive customization options:
- Multiple Domains Support: Configure multiple callback domains with rotation strategies
- Domain Rotation Strategies:
round-robin: Rotates through domains for each requestfail-over: Switches to next domain after consecutive failures
- Traffic Transforms: Apply custom transformations to C2 traffic:
- Base64/Base64URL encoding
- XOR encryption
- Prepend/append custom data
- Message Placement Options: Place C2 messages in:
- HTTP headers
- URL parameters
- Cookies
- Request body
- Custom Headers: Define custom HTTP headers for C2 requests
Installation
- Copy the this entire repository to your Mythic C2 Server
- Use
mythic-clito install the folder../mythic-cli install folder /path/to/dark-agent
Supported Commands
Dark Agent implements commands across two categories: Built-in Commands (native Crystal implementations) and BOF-based Commands (C object files loaded at runtime).
| Command | Description | Type | Linux | macOS | Browser Scripts | MITRE ATT&CK |
|---|---|---|---|---|---|---|
| bof_exec | Execute a previously loaded BOF with arguments | Built-in | Yes | Yes | T1059 | |
| bof_list | List all currently loaded BOFs | Built-in | Yes | Yes | ||
| bof_load | Load a BOF into memory without registering as command | Built-in | Yes | Yes | T1129 | |
| bof_purge | Remove all BOFs from memory* | Built-in | Yes | Yes | ||
| bof_unload | Unload a specific BOF from memory | Built-in | Yes | Yes | ||
| download | Download file from target system (supports chunked transfers) | Built-in | Yes | Yes | T1020, T1030, T1041 | |
| exit | Terminate the agent | Built-in | Yes | Yes | ||
| jobkill | Kill a running BOF job by task ID | Built-in | Yes | Yes | ||
| jobs | List active BOF jobs with runtime information | Built-in | Yes | Yes | ||
| load | Load a BOF and register it as a Mythic command | Built-in | Yes | Yes | T1129 | |
| ls | List files in a directory with detailed metadata | Built-in | Yes | Yes | Yes | T1083 |
| sleep | Change agent sleep/jitter intervals | Built-in | Yes | Yes | ||
| socks | Start or stop a SOCKS5 proxy server on specified port | Built-in | Yes | Yes | T1090 | |
| unload | Unload a command from memory | Built-in | Yes | Yes | ||
| upload | Upload file to target system | Built-in | Yes | Yes | T1105 | |
| arp | Display ARP table information | BOF | Yes | Yes | Yes | T1016 |
| cat | Display file contents | BOF | Yes | Yes | T1005 | |
| chmod | Change file permissions | BOF | Yes | Yes | T1222.002 | |
| chown | Change file ownership | BOF | Yes | Yes | T1222.002 | |
| coffee | Test BOF execution (example "coffee brewing" command) | BOF | Yes | Yes | ||
| df | Display filesystem disk space usage with mount analysis | BOF | Yes | Yes | Yes | T1082 |
| env | Display environment variables | BOF | Yes | Yes | T1082 | |
| hostname | Display system hostname | BOF | Yes | Yes | T1082 | |
| ifconfig | Display network interface configuration | BOF | Yes | Yes | Yes | T1016 |
| kill | Terminate processes by PID | BOF | Yes | Yes | T1562.001 | |
| krb_dump_kirbi | Dump credentials from a Kerberos credential cache | BOF | Yes | T1558.005 | ||
| krb_listccaches | Enumerate all Kerberos credential caches | BOF | Yes | T1558.005 | ||
| last | Show last logged in users from wtmp log | BOF | Yes | Yes | T1033 | |
| mkdir | Create directory and any necessary parent directories | BOF | Yes | Yes | T1059 | |
| mounts | List all mounted filesystems with security analysis | BOF | Yes | Yes | Yes | T1082 |
| mv | Move or rename files and directories | BOF | Yes | Yes | T1070.006 | |
| netstat | Display network connections and routing tables | BOF | Yes | Yes | T1049 | |
| nslookup | Perform DNS lookups with optional custom nameserver | BOF | Yes | Yes | Yes | T1018 |
| portscan | Scan for open ports on target hosts | BOF | Yes | Yes | T1046 | |
| ps | List running processes with detailed information | BOF | Yes | Yes | Yes | T1057 |
| rm | Remove files and directories | BOF | Yes | Yes | T1070.004 | |
| routes | Display system routing table | BOF | Yes | Yes | Yes | T1016 |
| shell | Execute shell commands on the target system | BOF | Yes | Yes | T1059.004 | |
| timestomp | Modify file timestamps for anti-forensics | BOF | Yes | Yes | T1070.006 | |
| uptime | Show system uptime and load averages | BOF | Yes | Yes | T1082 | |
| whoami | Display current user information | BOF | Yes | Yes | T1033 |
Each command (like hostname and ifconfig) is implemented using a BOF file. When you use the command:
- The command uses
bof_executeto run the associated BOF - If the BOF hasn't been loaded yet, you must first use
load [command]to load it - For example:
load hostnamefollowed byhostname
Creating Custom BOFs
Writing a BOF is straightforward. Include beacon.h and implement coffee(). The framework handles loading, execution, and sending output back to the operator.
Minimal Example
#include "../includes/beacon.h"
#include <sys/stat.h>
#include <errno.h>
void coffee(int argc, char **argv) {
if (argc < 1) { BeaconPrintf("Usage: example <path>"); return; }
const char *path = argv[0];
struct stat st;
// Simple status message
BeaconPrintf("checking path: %s", path);
if (stat(path, &st) != 0) {
BeaconPrintf("error: %s", strerror(errno));
return;
}
// JSON output for browser script rendering
bof_result_t *r = bof_result_create(512);
bof_result_append(r, "{");
bof_field_str(r, "path", path);
bof_field_ull(r, "size", (unsigned long long)st.st_size);
bof_field_uint(r, "mode", (unsigned int)st.st_mode);
bof_result_trim(r);
bof_result_append(r, "}");
bof_result_send(r);
bof_result_destroy(r);
}
Compile it, drop the .o into the payload, load it in Mythic. Done.
Output API
| Function | Output |
|---|---|
BeaconOutput(buf, len) |
send raw bytes to the operator |
BeaconPrintf("found %d user=%s", n, u) |
status/debug message that supports %d %s %p %x |
bof_result_append(r, "text") |
text |
bof_field_str(r, "name", "ls") |
"name":"ls", |
bof_field_int(r, "pid", 1234) |
"pid":1234, |
bof_field_uint(r, "uid", 501) |
"uid":501, |
bof_field_ull(r, "size", 102400) |
"size":102400, |
bof_field_hex(r, "flags", 0x405) |
"flags":"0x405", |
bof_result_append_mac(r, mac) |
aa:bb:cc:dd:ee:ff |
bof_result_trim(r) |
strips trailing comma |
bof_result_send(r) |
sends via BeaconOutput |
bof_result_destroy(r) |
free |
BeaconOutput and BeaconPrintf are the most common ways to write data from a BOF. The bof_result_t JSON builder is primarily useful when pairing with a Mythic browser script for structured UI rendering.
bof_result_t *r = bof_result_create(4096);
bof_result_append(r, "{\"entries\":[{");
bof_field_str(r, "name", proc_name);
bof_field_int(r, "pid", pid);
bof_field_ull(r, "size", file_size);
bof_field_hex(r, "flags", flags);
bof_result_trim(r);
bof_result_append(r, "}]}");
bof_result_send(r);
bof_result_destroy(r);
// → {"entries":[{"name":"ls","pid":1234,"size":102400,"flags":"0x405"}]}
Arguments
BOFs receive (int argc, char **argv). Mythic passes arguments two ways:
Split Arguments (bof_args)
Space-separated → individual argv entries:
"192.168.1.1 22,80,443"→argv[0]="192.168.1.1",argv[1]="22,80,443"- Best for BOFs with structured parameters (paths, modes, hosts)
- Example:
portscan 192.168.1.1 22,80,443
Single String (bof_args_str)
Full string → argv[0]:
"ls -latr /tmp"→argv[0]="ls -latr /tmp"- Best for BOFs that pass a command through as-is
- Example:
shell ls -latr /tmp
Building
# Linux
gcc -fPIC -c your_bof.c -o your_bof.o -I src/bofs/includes
# Build all BOFs (runs inside the Mythic build container)
./build.sh -b # Linux
./build.sh -B # macOS (aarch64, requires Zig + macOS SDK)
Usage
Running the Agent
# Run the debug version
./output/dark-agent-debug
# Run the release version
./output/dark-agent
Direct Mode
Dark Agent can be built in "direct mode", which creates a standalone COFF loader without any Mythic C2 functionality. This is useful for testing BOFs without needing a full Mythic server.
In direct mode, the agent:
- Loads the specified COFF file
- Executes the
coffee()function from the BOF - Passes any additional command-line arguments to the BOF
- Displays any output produced by the BOF
This mode is ideal for BOF development and testing before deploying to a full Mythic environment.
Example usage:
# Build direct mode version
./build.sh -D
# Have a cup of COFFee
./output/dark-agent-direct output/bofs/coffee.o
Dark-Agent
- 97
- 12
- 1
- 0
- 0
- about 4 hours ago
- August 12, 2026
Fri, 04 Sep 2026 14:26:49 GMT