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.
Disclaimer
This tool is intended for use by authorized security professionals only. Use on systems or networks without explicit written permission is illegal and unethical. The authors accept no liability for misuse or damage caused by this software. By using this tool you confirm you have authorization to do so.
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
- 7
- 1
- 0
- 0
- 0
- about 4 hours ago
- August 12, 2026
GNU General Public License v3.0
Thu, 13 Aug 2026 18:55:57 GMT