Inital upload
inital upload
This commit is contained in:
parent
591d0a3a2e
commit
78bbcb7ebb
3 changed files with 376 additions and 0 deletions
27
check_config.py
Normal file
27
check_config.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
import sqlite3
|
||||
|
||||
def load_config():
|
||||
conn = sqlite3.connect("config.db")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT tg_bot_token, chat_id, cb, ma, delay, port FROM config LIMIT 1")
|
||||
config = cursor.fetchone()
|
||||
conn.close()
|
||||
if not config:
|
||||
raise ValueError("No configuration")
|
||||
return {
|
||||
"tg_bot_token": config[0],
|
||||
"chat_id": config[1],
|
||||
"cb": config[2],
|
||||
"ma": config[3],
|
||||
"delay": config[4],
|
||||
"port": config[5],
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
config = load_config()
|
||||
print("Configuration:")
|
||||
for key, value in config.items():
|
||||
print(f"{key}: {value}")
|
||||
except ValueError as e:
|
||||
print(e)
|
229
main.py
Normal file
229
main.py
Normal file
|
@ -0,0 +1,229 @@
|
|||
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())
|
120
setup.py
Normal file
120
setup.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
import sqlite3
|
||||
import os
|
||||
import paramiko
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def create_database():
|
||||
conn = sqlite3.connect("config.db")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
tg_bot_token TEXT NOT NULL,
|
||||
chat_id TEXT NOT NULL,
|
||||
cb TEXT NOT NULL,
|
||||
ma INTEGER NOT NULL,
|
||||
delay INTEGER NOT NULL,
|
||||
port INTEGER NOT NULL
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
kf = "server_key.pem"
|
||||
|
||||
|
||||
def generate_rsa_key(key_filename=kf, key_size=2048):
|
||||
key = paramiko.RSAKey.generate(key_size)
|
||||
key.write_private_key_file(key_filename)
|
||||
print(f"Key generated: {key_filename}")
|
||||
print("Goodbye!")
|
||||
|
||||
|
||||
def delete_existing_key():
|
||||
delete_input = input(
|
||||
f"Key {kf} exists. Delete it? (y/n): ").strip().lower()
|
||||
if delete_input == 'y':
|
||||
os.remove(kf)
|
||||
print(f"Key {kf} deleted.")
|
||||
generate_rsa_key()
|
||||
else:
|
||||
print('Okay, goodbye!')
|
||||
|
||||
|
||||
def prompt_for_key_generation():
|
||||
if os.path.exists(kf):
|
||||
delete_existing_key()
|
||||
else:
|
||||
generate_rsa_key()
|
||||
|
||||
|
||||
def handle_user_input():
|
||||
user_input = input("Enter 1 to generate the key or 0 to exit: ").strip()
|
||||
if user_input == "1":
|
||||
prompt_for_key_generation()
|
||||
elif user_input == "0":
|
||||
print("Bye")
|
||||
else:
|
||||
print("Invalid input. Please try again.")
|
||||
|
||||
|
||||
def main1():
|
||||
handle_user_input()
|
||||
|
||||
|
||||
def save_config(tg_bot_token, chat_id, cb, ma, delay, port):
|
||||
conn = sqlite3.connect("config.db")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM config")
|
||||
cursor.execute("""
|
||||
INSERT INTO config (tg_bot_token, chat_id, cb, ma, delay, port)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""", (tg_bot_token, chat_id, cb, ma, delay, port))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def check(prompt: str, condition: Callable[[int], bool], default=None) -> int:
|
||||
while True:
|
||||
try:
|
||||
raw = input(prompt + ": ").strip()
|
||||
|
||||
if not raw and default is not None:
|
||||
return default
|
||||
|
||||
value = int(raw)
|
||||
if condition(value):
|
||||
return value
|
||||
print("Incorrect value, please try again..")
|
||||
except ValueError:
|
||||
print("You must enter an integer value.")
|
||||
|
||||
|
||||
def main():
|
||||
print("Сonfigure honeypot for yourself:")
|
||||
|
||||
while True:
|
||||
tg_bot_token = input("Telegram Bot Token: ").strip()
|
||||
if not tg_bot_token:
|
||||
print("Token can't be blank")
|
||||
else:
|
||||
break
|
||||
|
||||
chat_id = check("Telegram Chat ID (botapi) ", lambda x: x < 0)
|
||||
cb = "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10"
|
||||
ma = check("Max Attempts (leave blank for default value of 3)",
|
||||
lambda x: x > 0, default=3)
|
||||
delay = check("Delay (in ms(leave blank for default value of 929)) ",
|
||||
lambda x: x >= 0, default=929)
|
||||
port = check("Port (leave blank for default value of 22)",
|
||||
lambda x: 0 < x < 65536, default=22)
|
||||
|
||||
save_config(tg_bot_token, chat_id, cb, ma, delay, port)
|
||||
print("The configuration is saved, now you can run honeypot")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_database()
|
||||
main()
|
||||
main1()
|
Loading…
Reference in a new issue