import asyncio import paramiko import socket import aiosqlite from aiosqlite import connect from aiogram import Bot, Dispatcher, types from aiogram.filters import Command from aiogram.types import FSInputFile import threading import time import matplotlib.pyplot as plt import os async def load_config(): """async def that loads config from a database """ async with aiosqlite.connect("config.db") as conn: async with conn.execute("SELECT tg_bot_token, chat_id, cb, ma, delay, port FROM config LIMIT 1") as cursor: config = await cursor.fetchone() if not config: raise ValueError("No configuration") return config async def send_msg(token, chat_id, message): """Fynction that let alerts about a login attempts in telegram chat exist Args: token (int): telegram bot token chat_id (int): telegram chat id with alerts in botapi standart message (int): message, that sending to chat when someone is trying to brute-force this honeypot """ bot = Bot(token=token) await bot.send_message(chat_id=chat_id, text=message) await bot.session.close() async def log_db(username, password, client_ip): """Async fynction wich logging all login attempts in database Args: username (str): username entered by attackers password (str): password entered by attackers client_ip (str): attackers ip """ async with aiosqlite.connect("honeypot_logs.db") as conn: await conn.execute('''CREATE TABLE IF NOT EXISTS logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT, ip TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )''') await conn.execute('INSERT INTO logs (username, password, ip) VALUES (?, ?, ?)', (username, password, client_ip)) await conn.commit() class FakeSSHServer(paramiko.ServerInterface): """Class wich raises ssh server using paramiko """ def __init__(self, client_ip, send_message_func, log_func, ma, delay): super().__init__() self.client_ip = client_ip self.send_message_func = send_message_func self.log_func = log_func self.auth_attempts = {} self.ma = ma self.delay = delay self.completion_event = threading.Event() def check_channel_request(self, kind, chanid): if kind == "session": return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): asyncio.run(self.log_func(username, password, self.client_ip)) asyncio.run(self.send_message_func( f"Login attempt from {self.client_ip}\nUsername: {username}\nPassword: {password}")) if username not in self.auth_attempts: self.auth_attempts[username] = 0 self.auth_attempts[username] += 1 if self.auth_attempts[username] >= self.ma: asyncio.run(self.send_message_func( f"Failed login attempts exceeded for {self.client_ip}" )) time.sleep(self.delay / 1000.0) return paramiko.AUTH_FAILED def handle_client(client_socket, client_address, tg_bot_token, chat_id, cb, ma, delay): """Fuction wich handles ssh clients Args: client_address (str): client ip adress tg_bot_token (str): telegram bot token chat_id (str): telegram chat id cb (str): banner, wich paramiko sends to clients ma (int): max attempts from a client delay (int): delay in answer """ transport = paramiko.Transport(client_socket) try: server_key = paramiko.RSAKey(filename="server_key.pem") transport.add_server_key(server_key) server = FakeSSHServer( client_ip=client_address[0], send_message_func=lambda msg: send_msg(tg_bot_token, chat_id, msg), log_func=log_db, ma=ma, delay=delay ) transport.local_version = cb transport.start_server(server=server) channel = transport.accept(20) if channel: channel.close() finally: transport.close() client_socket.close() async def start_server(tg_bot_token, chat_id, cb, ma, delay, port): """Starts a paramiko ssh server Args: tg_bot_token (str): telegram bot token chat_id (str): telegram chat id cb (str): banner, wich paramiko sends to clients ma (int): max attempts from a client delay (int): delay in answer to client port (ште): port on which the ssh server is raised """ server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(("", port)) server_socket.listen(100) print(f"Listening for connections on port {port}...") server_socket.setblocking(False) loop = asyncio.get_event_loop() while True: client_socket, client_address = await loop.sock_accept(server_socket) threading.Thread(target=handle_client, args=( client_socket, client_address, tg_bot_token, chat_id, cb, ma, delay)).start() async def tg_bot(tg_bot_token, chat_id): """Async function with a telegram bot Args: tg_bot_token (str): variable with a telegram bot token chat_id (int): variable with a telegram chat id """ bot = Bot(token=tg_bot_token) dp = Dispatcher() @dp.message(Command('start')) async def cmd_start(message: types.Message): if str(message.chat.id) == chat_id: await message.answer("90") @dp.message(Command("stat_ip")) async def statip(message: types.Message): if str(message.chat.id) == chat_id: async with connect("honeypot_logs.db") as conn: async with conn.execute("SELECT ip, COUNT(*) AS attempts FROM logs GROUP BY ip ORDER BY attempts DESC") as cursor: rows = await cursor.fetchall() stats = "\n".join([f"{ip}: {count}" for ip, count in rows]) await message.answer(f"IP Stats:\n{stats}") @dp.message(Command("stat_ip_chart")) async def statip_chart(message: types.Message): if str(message.chat.id) == chat_id: async with connect("honeypot_logs.db") as conn: async with conn.execute("SELECT ip, COUNT(*) AS attempts FROM logs GROUP BY ip ORDER BY attempts DESC") as cursor: rows = await cursor.fetchall() if not rows: await message.answer("No data") return labels = [row[0] for row in rows] sizes = [row[1] for row in rows] plt.figure(figsize=(8, 8)) plt.pie( sizes, labels=None, autopct='%1.1f%%', colors=plt.cm.tab20.colors[:len(sizes)] ) plt.legend(labels=[f"{ip}: {count}" for ip, count in rows], loc="lower left", bbox_to_anchor=(1, 0)) plt.title("Shares of IP addresses from the total array") plt.savefig("temp.png", bbox_inches="tight") plt.close() png_path = os.path.join(os.path.dirname(__file__), 'temp.png') await message.answer(f'{png_path}') png = FSInputFile(png_path) await message.answer_photo(photo=png, caption='Ip statistic graph') os.remove(png_path) await dp.start_polling(bot) async def main(): """loads all other functions """ tg_bot_token, chat_id, cb, ma, delay, port = await load_config() chat_id = str(chat_id) await asyncio.gather( start_server(tg_bot_token, chat_id, cb, ma, delay, port), tg_bot(tg_bot_token, chat_id) ) if __name__ == "__main__": asyncio.run(main())