Custom C2 Development #2 — CloakCat vs Cobalt Strike: A Feature Parity Analysis
Author
CloakCat
Time
9 min read
Read by
40
Prologue
The previous post covered CloakCat's design philosophy and architecture. With Phase 0 through 8 fully wrapped, it's time to ask the uncomfortable question: benchmarked against Cobalt Strike — how far have we come, and where are the gaps?
This isn't a feelings piece. It's a code-level teardown — ~14,700 LOC across the entire workspace, dissected crate by crate and mapped 1:1 against CS's core feature set.
1. Current Scale
| Crate | LOC | Files | Role |
|---|---|---|---|
| cat-agent | 6,096 | 20 | Beacon implant |
| cat-server | 2,817 | 13 | Team server |
| cat-cli | 2,079 | 15 | Operator CLI |
| cloakcat-protocol | ~1,000 | 7 | Shared types / crypto |
| tools/shellcode-loader | ~2,700 | 20+ | Shellcode loader |
| Total | ~14,700 | 75+ |
The agent accounts for 41% of the codebase. That tracks — post-ex (token manipulation, lateral movement), the BOF loader, and the sRDI converter all live there. The server went through a full refactor; the handler/service/db layers are cleanly separated. The protocol crate was intentionally kept lean.
2. Feature Parity Against Cobalt Strike
What's Working
Beacon Check-in Loop — 90%
Sleep + Jitter + exponential backoff are all implemented. The server-side long-poll runs on tokio::sync::Notify, which is more efficient than CS's busy-wait approach. The missing 10% is CS's interactive mode — sleep 0, real-time response. Not there yet.
Listeners (HTTP/HTTPS) — 85%
Multiple concurrent listeners, runtime add/remove, and automatic self-signed certificate generation via rcgen all work. What's missing: redirectors and domain fronting. Part of that gap is infrastructure, not code — you can't fully solve it in the codebase alone.
Malleable C2 Profiles — 70%
A TOML-based profile engine at 471 LOC. Body transforms (XOR, base64, prepend/append) and header customization are functional. Missing relative to CS: data_jitter, http-stager, dns-beacon, and process-inject blocks. TOML parses more reliably than CS's custom DSL, but the expressiveness ceiling is lower.
Shell Command Execution — 95%
300-second timeout with 1MB output cap — functionally equivalent to CS, and arguably more defensively coded. A sleep 999999 command won't hang the beacon.
File Transfer — 80%
512KB chunked upload/download, functional up to 50MB. No download queuing or progress bar UX matching CS, but operationally sufficient.
Token Manipulation — 75%
Core triad implemented: steal_token, make_token, rev2self. Missing relative to CS: getuid, getsystem, and an automated privilege escalation pipeline. make_token is limited to LOGON32_LOGON_NEW_CREDENTIALS, so it applies only to network authentication — it doesn't affect local process creation. That's a meaningful behavioral difference from CS.
Lateral Movement — 70%
PsExec (SCM-based) and WMI (direct COM vtable calls) at 613 LOC. The WMI implementation manually computes vtable offsets rather than using windows-sys COM bindings — minimizes external dependencies, but maintenance overhead is real. Missing: jump winrm, DCOM, pass-the-hash.
BOF Execution — 80%
COFF parser (535 LOC) + in-memory loader (394 LOC) + BeaconAPI compatibility layer (368 LOC, 15+ functions). AMD64 relocations (REL32, ADDR64, ADDR32NB) are handled. Execution is thread-isolated with a 30-second timeout. Known bug: BeaconPrintf does not expand C varargs — %s and %d format strings pass through literally. Real BOF output will be garbled.
SOCKS5 Proxy — 75%
Reverse SOCKS5 implemented to RFC 1928 (server 373 LOC + agent 174 LOC). Per-session 5-minute timeout, 10-session cap per agent. Extended proxychains nmap scans or high-connection workflows will hit these limits.
sRDI — 85%
PE parser (1,054 LOC) + DLL-to-shellcode converter (1,693 LOC). PEB walking, import resolution, and relocation patching all work. PE header zeroing is supported. Stub validation via r2 disassembly confirmed correct sequencing: PEB access → VirtualAlloc → section mapping → IAT resolution → DllMain invocation.
What CS Has That CloakCat Doesn't — Priority Order
This is where honesty matters.
Sleep Mask — Critical Priority. While the beacon sleeps, code sits in memory as plaintext. A single Windows Defender memory scan will catch it. The StageConfig.sleep_mask field exists in malleable.rs — the implementation does not. Non-negotiable for EDR survival.
Process Injection — Critical Priority. Every post-ex capability currently executes inside the agent process. If the agent dies, everything dies with it. No equivalent to CS's inject, shinject, or dllinject — process migration is not possible.
execute-assembly — Critical Priority. CLR hosting for in-memory .NET assembly execution. Without it, Rubeus, Seatbelt, and SharpHound cannot run. BOF covers some of the gap, but CRTO scenarios are roughly 90% dependent on those tools.
SMB Beacon (Named Pipe Pivoting) — High Priority. Chaining hosts that cannot communicate directly with the external C2 channel. HTTP alone cannot reproduce internal pivot scenarios.
DNS Beacon — High Priority. Slow but maximally covert — DNS TXT/A record-based C2 channel. The Transport trait is already in place; DnsTransport is the implementation gap.
3. Where CloakCat Has the Edge
Feature gap analysis tends to surface deficiencies. At the code level, there are areas where CloakCat is objectively ahead.
Cryptographic Design. CS uses a single shared key for both authentication and signing. CloakCat derives separate auth and signing keys from the master key via HKDF-SHA256. HMAC messages include null-byte delimiters between fields to prevent length-confusion substitution attacks. This is a more cryptographically sound protocol design than CS's.
Server Architecture. CS's team server is a Java monolith. CloakCat's 4-crate workspace enforces unidirectional dependencies (protocol ← everything else), with a clean handler → service → db separation inside the server. 462 + 601 + 514 = 1,577 LOC with clear boundaries.
Structured Audit Logging. CS writes to a teamserver.log flat file. CloakCat stores actor, action, target_type, target_id, and context (JSONB) in PostgreSQL — queryable and filterable. (The irony: the audit table migration is missing, so this currently throws a runtime error.)
sqlx Compile-Time Query Validation. Every SQL statement in db.rs is type-checked at build time. Runtime SQL bugs that would surface in CS's Java codebase are structurally eliminated.
sRDI Code Generation Safety. The 1,693 LOC sRDI converter generates raw x86_64 machine code. Rust's Asm struct + emit()/patch_rel32() pattern reduces byte-manipulation errors significantly. Writing the equivalent in C carries substantially higher buffer overflow risk.
Build Automation. CS ships a GUI-based Payload Generator. CloakCat runs build-agent --format shellcode --encrypt from the CLI — DLL build → sRDI conversion → AES-256-GCM encryption in a single pipeline. Scripting and CI integration are first-class.
4. Honest Assessment of the Gaps
Operationally Problematic
Lateral movement writes to disk. deploy_payload() in lateral.rs copies the agent binary to \\target\ADMIN$ and executes via service or WMI. Every EDR detects this pattern. CS can spawn+inject shellcode directly into memory. CloakCat can't — no process injection means file drop is mandatory.
BOF output is broken. BeaconPrintf does not expand C varargs. Format strings %s and %d output literally. Any real BOF tool produces unreadable results. The beacon_api.rs comment explicitly notes "varargs not expanded."
SOCKS5 timeouts are aggressive. Five minutes per session, ten sessions per agent. Extended scanning operations or high-concurrency workflows will drop connections mid-run.
Shellcode has no self-decryption. Build-time AES-256-GCM encryption is in place, but the agent contains no decryption logic. The external loader (tools/shellcode-loader) is a hard dependency — a separate stager or dropper is always required.
OPSEC Posture
No Sleep Mask. Code is plaintext in memory during sleep. Modern EDR periodic memory scanning will catch this immediately.
No Process Isolation. All operations execute in the agent process. One crash takes everything down.
No ETW/AMSI Patching. .NET execution hits AMSI. Every Windows API call generates ETW events.
No Direct Syscalls. All Windows API calls route through windows-sys or LoadLibraryA/GetProcAddress. EDR userland hooks have full visibility.
Code Quality Issues
Several bugs warrant immediate attention.
The audit table migration is missing. insert_audit() and list_audit() exist; the migration creating the actual table (0006) does not. sqlx throws a runtime error. cat-cli and cloakcat-protocol both have edition = "2024" in Cargo.toml — that edition doesn't exist; it should be 2021. error.rs on the server uses eprintln! instead of tracing, breaking logging consistency. The tunnel manager's gc() is never called automatically — closed SOCKS5 sessions accumulate in memory.
Test coverage is inadequate. Two inline tests in tls.rs, zero tests across service/handler/DB layers, and integration testing that depends entirely on manual checklists.
5. Roadmap
Immediate Fixes
| Item | Action | Impact |
|---|---|---|
| Audit migration | Add 0006_init.sql | Eliminates runtime error |
| BeaconPrintf varargs | Implement %s/%d/%x minimum format support | Significant BOF compatibility improvement |
| Tunnel GC automation | Periodic gc() invocation | Prevents memory accumulation |
| Cargo.toml edition | 2024 → 2021 | Removes build warnings |
| eprintln → tracing | Fix error.rs | Restores logging consistency |
| token_b64 legacy removal | DB column + code cleanup | Reduces complexity |
Near-Term
| Item | CRTO Relevance | Description |
|---|---|---|
| Process Injection | Required | VirtualAllocEx + WriteProcessMemory + CreateRemoteThread. Foundation for process migration |
| execute-assembly | Required | CLR hosting for in-memory .NET execution. Prerequisite for Rubeus/Seatbelt |
| Sleep Mask | Critical | XOR-encrypt code sections before sleep, decrypt on wake. Defeats EDR memory scanning |
| SMB Beacon | Required | Named pipe-based internal pivot C2 channel |
| Staged Payload | Useful | Stager delivers minimal shellcode; full beacon downloaded from server |
Long-Term
| Item | Description |
|---|---|
| Direct Syscalls | Direct ntdll invocation to bypass EDR userland hooks |
| ETW/AMSI Patching | Runtime patching of EtwEventWrite/AmsiScanBuffer |
| DNS Beacon | Covert C2 channel over DNS TXT/A records |
| Domain Fronting | Network detection evasion via CloudFront/Azure CDN |
| PPID Spoofing | Parent process ID manipulation on process creation |
| Module Stomping | Load shellcode into legitimate DLL memory regions |
6. Closing
By the numbers, CloakCat reproduces roughly 70–80% of Cobalt Strike's core feature set — beacon check-in, listeners, Malleable C2, file transfer, BOF, SOCKS5, token manipulation, lateral movement, sRDI. For a solo Rust implementation, that's a defensible position.
The remaining 20–30% is the problem. Process Injection, execute-assembly, Sleep Mask — without those three, the beacon doesn't survive a minute in an EDR-enabled environment. These are areas CS has refined commercially over years, and closing that gap is a long road.
The value of this project was never in the completion percentage. Disassembling the sRDI x86_64 stub in r2 and tracing PEB walking line by line, manually computing WMI COM vtable offsets and developing a concrete model of how Windows COM infrastructure is organized — that's a category of understanding you cannot acquire by operating any tool, no matter how long.
The next post covers Process Injection implementation — the VirtualAllocEx → WriteProcessMemory → CreateRemoteThread chain in Rust, with a specific focus on why this pattern is detected and what modifications reduce that detection surface.
This is the second post in the CloakCat development series. Phase-by-phase updates will continue as development progresses.
Previous: Custom C2 Development #1 — Rust C2 Framework Architecture Review