ApeX v0.4: Metis

ApeX C2 v0.4 is live. After months in alpha with design partners, we’re shipping the first release of our post-exploitation framework. We built ApeX for the way red teams actually work today: human operators driving engagements through a modern web UI, agentic operators driving them through a native MCP interface, and compile-time OPSEC that doesn’t ask either side to trade evasion for operator experience.
The TLDR: a Zig-compiled agent for Windows (Linux and macOS coming soon), paired with a modern web operator interface, deployable self-hosted and air-gapped. On-agent LuaJIT so complex recon runs in a single task. Native MCP integration so an LLM can drive engagements under scoped tokens with a full audit trail. Fully customizable network communication down to the packet structure. Full Cobalt Strike BOF compatibility imported through Havoc-style Python aggressor scripts, in-process .NET assembly execution, and image-backed call stacks by architecture.
The name. Metis represents intelligence applied through deception and foresight rather than brute force. Metis combines cunning, strategic intelligence, and careful planning. For a C2 built around intelligence and operational discretion as inseparable design principles, Metis fit.
The full product page is at /apex, the OPSEC matrix at /apex/opsec, and the head-to-head against Cobalt Strike 4.13, Brute Ratel C4 2.3, and Nighthawk is at /apex/compare. The rest of this post walks through what’s in v0.4 and credits the public research the techniques build on.
1. AI operator via MCP
ApeX is the only C2 framework with a native agentic AI operator interface. Connect any MCP-compatible LLM (Claude Code, Cursor, Codex) to the teamserver via a scoped token, and it can enumerate targets, author and upload modules, run them, read results, and adapt its next move based on what it saw. 25+ tools across 15 scopes. Every tool call is logged with arguments, agent ID, result, and duration. Tokens are per-user, scoped, and instantly revocable.
The LLM reads embedded reference docs (Lua API, command reference, BOF conventions, profile format, aggressor SDK) and creates the right move for the situation. Give it read-only scopes to review an engagement; give it execute scopes to run modules under your supervision; give it full autonomy where that’s appropriate for the engagement.
Full tool matrix and example sessions at /apex/agentic-operations.

2. On-agent LuaJIT
Most C2s require a round-trip to the teamserver for every post-exploitation step. ApeX embeds a sandboxed LuaJIT VM directly on the agent with a comprehensive post-exploitation API: filesystem, processes, tokens, registry, services, crypto, named pipes, network enumeration. LuaJIT FFI gives direct Win32 access when the API doesn’t already cover what you need.
Complex recon or privilege-escalation runs in a single task instead of a dozen heartbeats, which cuts C2 traffic and lowers detection risk. Writing one Lua module that scans token privileges, enumerates SYSTEM services, and probes named-pipe impersonation candidates is a short script that returns one structured result, not a sequence of a dozen individual commands.
Why this matters for AI operators. The MCP interface in Section 1 and the on-agent Lua VM here are designed as a pair. An LLM with a scoped teamserver token can author a new Lua module on the spot in response to what the current engagement needs, upload it, run it, and read the structured result, all inside one session, every step logged. That’s a build-test-execute loop a human can supervise.
In our launch demo we gave Claude Code a scoped MCP token and asked it to write a custom host-recon Lua module for a live agent. Under 90 seconds later it had pulled the ApeX Lua API reference through MCP, authored the module, uploaded it, and executed it on the target: process enumeration, defender and service state, network connections, the lot. The LLM composed ApeX-native API calls directly, no trial-and-error BOF wrangling, no cross-compile round trips.
Doing the same with a BOF is much harder for an AI to pull off end-to-end: writing C against Beacon API conventions, cross-compiling for the right target, matching the BOF ABI, writing a Python aggressor wrapper that describes the arguments, and verifying the result doesn’t tear down the agent, all without being able to iterate interactively against a real target. For the modules we ship, we do that work ahead of time; for ad-hoc capability an AI needs to invent in-session, Lua is the right surface. BOFs stay the right choice when you need existing library code or native-compiled performance.

3. Protocols and profiles
Agent-to-teamserver comms live in two layers. Protocols are packet-level definitions: wire format, handshake, encryption, messaging, and the payload injection point. Profiles (.apex format) sit on top, picking a protocol, binding a payload, and chaining transforms.
HTTP, HTTPS, and DNS (TXT/CNAME/A) ship as the core protocols. SMB2 and SSH2 ship as worked examples: full protocol-and-profile pairs that double as reference implementations for building your own. Both SMB2 and SSH2 are real implementations with real handshakes, encryption, and messaging on the wire, not tunnels. A packet-level inspector sees actual SMB2 or SSH traffic.
Custom protocols are first-class. The protocol editor lets you lay out packet structure and the payload injection point, and the UI renders a wire diagram so you can see exactly what the packet will look like before you ship.
Profile transforms chain in any order on top of the chosen protocol: TLS with SNI spoofing where the protocol is TLS-capable, and payload transforms including Base64, Base32, Hex, ROT13, prepend/append, and AES-256-GCM.
Here’s a trimmed slice of the SMB2 IOCTL profile: static SMB2 field values, dynamic helpers ($seq for message IDs, $ref for IDs captured during the handshake stages, $sizeof for the payload length), the --- separator between envelope and payload, and a payload transform chain.
[meta]
protocol "smb2"
max_payload_bytes "65536"
[request]
protocolid: 0x424D53FE
command: 0x000B
messageid: $seq(0, 1)
treeid: $ref("tree_connect.treeid")
sessionid: $ref("session_setup.sessionid")
ctlcode: 0x0011C017
inputcount: $sizeof(payload)
rq_maxin: 65536
rq_iflags: 1
---
${payload -> encrypt("<base64_key>")}

4. BOF, BOF-PE, and in-process CLR
ApeX runs Cobalt Strike Beacon Object Files directly. The compiled COFF (and extended BOF-PE) object is universal, the same artifact the rest of the ecosystem produces. What differs is how the C2 learns a BOF’s arguments: ApeX takes Havoc-style Python aggressor scripts, not Cobalt Strike’s Sleep-language Aggressor Scripts. Both object formats auto-detect at runtime. Havoc and Havoc Pro BOF packages port directly, and standalone COFF BOFs from the Cobalt Strike library run with a short Python aggressor wrapper. The reference COFF-loading conventions we build on come from TrustedSec’s COFFLoader.

The important addition: sensitive Win32 APIs called from BOFs are intercepted and executed through the agent’s own OPSEC safe infrastructure. The BOF sees a normal API return. The EDR sees a genuine, image-backed call stack originating in ntdll.dll or kernelbase.dll, not in some anonymous RWX page.
For .NET assemblies, we host the CLR in-process directly: no fork-and-run, no rundll32 surrogate. Our implementation builds on prior art including go-buena-clr and Being A Good CLR Host by passthehashbrowns. The practical result: assemblies like Rubeus and Seatbelt run in-process with clean OPSEC, no spawning a child just to hand off bytes. Two things to call out:
- Patchless AMSI bypass. The CLR loading path avoids the AMSI scanning surface by architecture, not by patching
AmsiScanBufferat runtime. Nothing to detect, nothing to signature. - Exit patching keeps the agent alive. A
.NETassembly that callsEnvironment.Exitwould normally tear down the hosting process. We patch the exit path so a misbehaving assembly closes its own AppDomain, not the agent.
Credit also to the broader in-memory execution ecosystem, especially Donut, whose patterns influenced how we think about shellcode-wrapped payloads.

5. OPSEC by architecture
Everything that makes the agent hard to catch is resolved at compile time and driven by a per-build polymorphism seed. No two builds share byte-level patterns. There is no runtime initialization overhead for the evasion layer because there is no runtime initialization: the obfuscation is baked into the binary before it ships.
A tour of what shipped in v0.4:
Mixed boolean-arithmetic obfuscation. Constants and arithmetic transformed into algebraically equivalent expressions, variable depth per call site, optimizer-resistant. This is the same family of techniques that Quarkslab’s work on MBA formalized.
RWX-less code paths. Code loading paths never create pages that are simultaneously writable and executable. The primary BOF execution path contains zero
VirtualProtectcalls and generates zeroNtProtectVirtualMemoryETW-Ti events: the two biggest tells for commodity memory scanners.Indirect syscalls. 36+ NT APIs resolved at runtime, in the family of Hell’s Gate and Halo’s Gate: SSN resolution with hook detection, and syscall execution via a
syscall; retgadget insidentdllso the opcode runs fromntdll’s own.text, not from agent memory.Sleep encryption of code and heap. Both the agent’s own code sections and heap allocations are encrypted during sleep, in the family of Ekko. The sleep call stack is indistinguishable from a normal thread waiting on a sync object: no timers, no APC callbacks, nothing that a scanner like Hunt-Sleeping-Beacons uses as a tell.
Clean, image-backed call stacks by architecture. Every execution path produces a stack whose every frame is
MEM_IMAGE. CET-compatible without gadget chaining or synthetic frames. See Microsoft’s developer guidance on hardware-enforced stack protection for what a CET-compatible process has to look like. Arbitrary function calls that would otherwise emerge from suspicious origins are routed through OS thread pool workers so the resulting trace reads as legitimate.Compile-time string encryption and API hashing. 526+ string literals encrypted at build time with per-string derived keys (wide-string / UTF-16 included). All API resolution via compile-time hashing: no plaintext function names in the binary.
Environmental guardrails. Domain, username, hostname, hardware ID, CIDR, activation date, working hours, working days, kill date. A keying failure triggers secure self-deletion. The agent only runs where you told it to run.
BOF API proxying, PE metadata spoofing, SNI spoofing,
.rdataentropy management, anti-debug, anti-VM, anti-forensics. The full matrix is at /apex/opsec.
All of the above are compile-time flags. Dial up what you need for the engagement; dial down what you don’t.
6. Deploy anywhere
ApeX is a docker compose up deployment. One bundle, four modes out of the box:
- Local dev: one-command stack-up for development against a disposable environment.
- Production: isolated listener containers behind the production compose file.
- Offline / air-gapped: a self-contained bundle with automated build, package, and smoke-test scripts. No internet required on the ops host.
- Edge: distributed listeners with per-edge mTLS certificate bootstrap. The control plane generates edge tarballs automatically.
Multiple listeners run simultaneously, each with its own TLS certificate (uploaded or auto-generated) and a static-site decoy for non-C2 traffic. Agent-to-agent pivoting runs over TCP or SMB named-pipe peer-to-peer links, with a parent-child relay mesh of up to 8 children per agent. A SOCKS5 proxy tunnels operator traffic through any agent. Automatic failover across configured C2 channels keeps you in position when the primary goes dark.
Operator experience
- Web UI. Modern dashboard with real-time push for every event (agent registrations, task results, listener state changes, module updates). Agent console with command palette. No legacy Java client.
- Auth. WebAuthn and passkeys. Two roles (Administrator, Operator). Session-level revocation. No passwords.
- Profiles. Visual editor for
.apexmalleable comms with real-time validation and rendered HTTP/DNS preview. - Vault. Versioned file storage for staging tools to push to agents.
- Audit. Full operator action trail: every command, every MCP tool call, every file access.

Where ApeX stands
The full feature-by-feature comparison lives at /apex/compare. The short version: ApeX pairs a native agentic MCP operator interface with a general-purpose on-agent scripting VM (LuaJIT with FFI). Call stacks are image-backed by architecture. The air-gapped and edge deployment pipelines are automated end-to-end.
Watch the demo
Access
ApeX ships through a vetted-access process. Request access with your team details and intended use case, or book a demo. We typically respond within one business day.
Immediately available in the United States, United Kingdom, EU, Australia, Canada, Iceland, Japan, New Zealand, Norway, and Switzerland (including Liechtenstein). Enquiries from other regions are subject to extended vetting; a short list of jurisdictions is not supported. The full list is on the access page.
Credit where it’s due: ApeX stands on a lot of public research. In addition to the specific links above, we lean on LuaJIT FFI for the on-agent VM, TrustedSec’s COFFLoader as a reference for BOF conventions, Havoc for the aggressor API our Python package system is compatible with, and the broader body of work from Cracked5pider, am0nsec, almounah, passthehashbrowns, thefLink, TheWover, and the many researchers whose names end up in the papers that these projects cite. Thanks to the whole offensive-security community for making the public literature we build on.
Questions, feedback, or ideas? Get in touch. Or if you want to go deep on a specific technique, we’ll do a technical post on it next.