diff --git a/dpg.ini b/dpg.ini index 616a576..ebeffd3 100644 --- a/dpg.ini +++ b/dpg.ini @@ -78,3 +78,8 @@ Pos=300,200 Size=700,400 Collapsed=0 +[Window][###118] +Pos=340,250 +Size=600,300 +Collapsed=0 + diff --git a/pyproject.toml b/pyproject.toml index c416649..06c004e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,8 @@ dependencies = [ "loguru (>=0.7.3,<0.8.0)", "dbus-python (>=1.4.0,<2.0.0)", "dearpygui (>=2.0.0,<3.0.0)", - "pyqt5 (>=5.15.11,<6.0.0)" + "pyqt5 (>=5.15.11,<6.0.0)", + "python-gvm (>=26.2.0,<27.0.0)" ] diff --git a/src/core/api/openvas.py b/src/core/api/openvas.py index c1f6180..2db3e89 100644 --- a/src/core/api/openvas.py +++ b/src/core/api/openvas.py @@ -1,76 +1,126 @@ -from gvm.connections import TLSConnection -from gvm.protocols.gmp import GMP +from gvm.connections import UnixSocketConnection +from gvm.protocols.gmp import Gmp +from gvm.errors import GvmError from src.utils.logger import get_logger logger = get_logger("openvas_scanner") class OpenvasScanner: - def __init__(self, hostname, port, username, password): - """Инициализация сканера OpenVAS""" + def __init__(self, socket_path='/var/lib/docker/volumes/greenbone-community-edition_gvmd_socket_vol/_data/gvmd.sock'): + """ + Инициализация сканера OpenVAS. + + :param socket_path: Путь к сокету GVM (по умолчанию '/var/run/gvmd.sock') + """ + self.socket_path = socket_path + self.connection = None + self.gmp = None + + def connect(self, username, password): + """ + Подключение к GVM. + + :param username: Имя пользователя для аутентификации + :param password: Пароль для аутентификации + """ try: - self.connection = TLSConnection(hostname=hostname, port=port) - self.gmp = GMP(self.connection) - self.gmp.authenticate(username, password) - self.configurations = {config.get_name(): config.get_id() for config in self.gmp.get_configurations().get_configurations()} - logger.info(f"Connected to GVM at {hostname}:{port}") - except Exception as e: - logger.error(f"Failed to connect to GVM: {e}") + self.connection = UnixSocketConnection(path=self.socket_path) + with Gmp(connection=self.connection) as gmp: + gmp.authenticate(username, password) + logger.info(f"Успешно подключено к GVM через {self.socket_path}") + except GvmError as e: + logger.error(f"Ошибка подключения к GVM: {e}") raise - + def get_configurations(self): - """Получить список конфигураций сканирования""" - return list(self.configurations.keys()) - - def start_scan(self, scan_name, targets, scan_config_name): - """Начать сканирование""" + """ + Получение списка конфигураций сканирования. + + :return: Список названий конфигураций + """ + try: + configs = self.gmp.get_scan_configs() + return [config.get('name') for config in configs] + except GvmError as e: + logger.error(f"Ошибка при получении конфигураций: {e}") + raise + + def create_target(self, name, hosts): + """ + Создание цели для сканирования. + + :param name: Имя цели + :param hosts: Список хостов (IP-адресов или сетей) + :return: ID созданной цели + """ + try: + target = self.gmp.create_target(name=name, hosts=hosts) + return target.get('id') + except GvmError as e: + logger.error(f"Ошибка при создании цели: {e}") + raise + + def create_task(self, name, config_name, target_id): + """ + Создание задачи сканирования. + + :param name: Имя задачи + :param config_name: Название конфигурации сканирования + :param target_id: ID цели + :return: ID созданной задачи + """ + try: + configs = self.gmp.get_scan_configs(filter=f"name={config_name}") + if not configs: + raise ValueError(f"Конфигурация '{config_name}' не найдена") + config_id = configs[0].get('id') + task = self.gmp.create_task(name=name, config_id=config_id, target_id=target_id) + return task.get('id') + except GvmError as e: + logger.error(f"Ошибка при создании задачи: {e}") + raise + + def start_task(self, task_id): + """ + Запуск задачи сканирования. + + :param task_id: ID задачи + """ try: - config_id = self.configurations.get(scan_config_name) - if not config_id: - raise ValueError(f"Конфигурация {scan_config_name} не найдена") - # Создать цель - target_response = self.gmp.create_target( - name=f"Target for {scan_name}", - hosts=targets.split(',') - ) - target_id = target_response.get_id() - # Создать задачу - task_response = self.gmp.create_task( - name=scan_name, - config_id=config_id, - target_id=target_id - ) - task_id = task_response.get_id() - # Запустить задачу self.gmp.start_task(task_id) - logger.info(f"Started scan with task ID: {task_id}") - return task_id - except Exception as e: - logger.error(f"Failed to start scan: {e}") + logger.info(f"Задача {task_id} запущена") + except GvmError as e: + logger.error(f"Ошибка при запуске задачи: {e}") raise - + def get_task_status(self, task_id): - """Получить статус задачи""" + """ + Получение статуса задачи. + + :param task_id: ID задачи + :return: Статус задачи + """ try: - tasks = self.gmp.get_tasks(filter_string=f"id={task_id}").get_tasks() - if tasks: - return tasks[0].get_status() - logger.warning(f"Task {task_id} not found") - return None - except Exception as e: - logger.error(f"Failed to get task status: {e}") + task = self.gmp.get_task(task_id) + return task.get('status') + except GvmError as e: + logger.error(f"Ошибка при получении статуса задачи: {e}") raise - + def get_report(self, task_id): - """Получить отчет по задаче""" + """ + Получение отчета по задаче. + + :param task_id: ID задачи + :return: Отчет в формате XML + """ try: - reports = self.gmp.get_reports(filter_string=f"task={task_id}") - if reports.get_reports(): - report_id = reports.get_reports()[0].get_id() - report = self.gmp.get_report(report_id).get_report() - logger.info(f"Retrieved report for task {task_id}") + reports = self.gmp.get_reports(filter=f"task_id={task_id}") + if reports: + report_id = reports[0].get('id') + report = self.gmp.get_report(report_id, format='xml') return report - logger.warning(f"No report found for task {task_id}") return None - except Exception as e: - logger.error(f"Failed to get report: {e}") + except GvmError as e: + logger.error(f"Ошибка при получении отчета: {e}") raise \ No newline at end of file diff --git a/src/ui/attack_window.py b/src/ui/attack_window.py index ba834c0..fb43dec 100644 --- a/src/ui/attack_window.py +++ b/src/ui/attack_window.py @@ -55,6 +55,9 @@ def build_attack_content(session): # Вкладка Modbus (атаки на Modbus) with dpg.tab(tag="modbus_attack_tab", label="Атаки на Modbus"): create_modbus_attack_tab() + + with dpg.tab(tag="scapy_tab", label="Scapy"): + logger.info("Вкладка Scapy нажата") def create_metasploit_tab(): """Вкладка Metasploit (заглушка)""" diff --git a/src/ui/main_window.py b/src/ui/main_window.py index b2f2a60..1d82841 100644 --- a/src/ui/main_window.py +++ b/src/ui/main_window.py @@ -17,7 +17,7 @@ def create_main_window(db): no_move=True, ): # Убраны фиксированные размеры # Заголовок с увеличенным шрифтом - dpg.add_text("Модуль тестирования безопасности", color=(255, 255, 255), pos=[600,0]) + dpg.add_text("Модуль тестирования безопасности", color=(255, 255, 255), pos=[500,0]) dpg.add_spacing(count=10) dpg.add_separator() diff --git a/src/ui/scanner_window.py b/src/ui/scanner_window.py index 63bacf2..24a29b6 100644 --- a/src/ui/scanner_window.py +++ b/src/ui/scanner_window.py @@ -3,6 +3,7 @@ from src.core.models.models import Session, Scan, Host, Port, Service from src.core.database.managers.scanner_manager import ScannerManager from src.core.api.nmap import NmapScanner from src.core.attacks.modbus_scan import ModbusScanner +from src.core.api.openvas import OpenvasScanner from src.utils.logger import get_logger import threading import time @@ -13,6 +14,7 @@ logger = get_logger("scanner_window") # Глобальные переменные для управления сканерами nmap_scanner = None modbus_scanner = None +openvas_scanner = None def create_scanner_window(db, session: Session, parent=None): """Окно сканирования (с поддержкой родительского контейнера)""" @@ -45,7 +47,7 @@ def build_scanner_content(scanner_service, session): create_nmap_tab(scanner_service, session) with dpg.tab(tag="openvas_tab", label="OpenVAS Scanning"): - dpg.add_text("OpenVAS scanning will be implemented here") + create_openvas_tab(scanner_service, session) with dpg.tab(tag="modbus_tab", label="Modbus Scanning"): create_modbus_tab(scanner_service, session) @@ -236,6 +238,154 @@ def run_nmap_scan(scanner_service, session, scan_id, scanner): ) scanner_service.update_scan(scan) +def create_openvas_tab(scanner_service, session): + """Вкладка OpenVAS сканирования""" + with dpg.group(): + # Группа для настроек подключения + with dpg.group(tag="openvas_connection_group"): + dpg.add_text("Настройки подключения к OpenVAS") + dpg.add_text("Сокет (например, /var/run/gvmd.sock):") + socket_input = dpg.add_input_text(tag="openvas_socket", default_value="/var/run/gvmd.sock", width=300) + dpg.add_text("Имя пользователя:") + username_input = dpg.add_input_text(tag="openvas_username", width=300) + dpg.add_text("Пароль:") + password_input = dpg.add_input_text(tag="openvas_password", password=True, width=300) + dpg.add_button( + label="Подключиться", + callback=lambda: connect_to_openvas( + dpg.get_value(socket_input), + dpg.get_value(username_input), + dpg.get_value(password_input) + ), + width=200, + height=40 + ) + dpg.add_spacer(height=10) + + # Группа для настроек сканирования (скрыта до подключения) + with dpg.group(tag="openvas_scan_group", show=False): + dpg.add_text("Настройки сканирования") + dpg.add_text("Имя сканирования:") + scan_name_input = dpg.add_input_text(tag="openvas_scan_name", width=300) + dpg.add_text("Цели (IP-адреса или сети, через запятую):") + targets_input = dpg.add_input_text(tag="openvas_targets", width=300) + dpg.add_text("Конфигурация сканирования:") + config_combo = dpg.add_combo(tag="openvas_config", items=[], width=300) + dpg.add_button( + label="Начать сканирование", + callback=lambda: start_openvas_scan( + scanner_service, + session, + dpg.get_value(scan_name_input), + dpg.get_value(targets_input), + dpg.get_value(config_combo) + ), + width=200, + height=40 + ) + dpg.add_spacer(height=10) + + # Область вывода + dpg.add_text("Вывод:") + dpg.add_input_text( + tag="openvas_output", + multiline=True, + height=300, + width=-1, + readonly=True + ) + +def connect_to_openvas(socket_path, username, password): + """Подключение к OpenVAS""" + global openvas_scanner + try: + openvas_scanner = OpenvasScanner(socket_path=socket_path) + openvas_scanner.connect(username, password) + configs = openvas_scanner.get_configurations() + dpg.configure_item("openvas_config", items=configs) + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + "✅ Подключено к OpenVAS\n") + dpg.configure_item("openvas_scan_group", show=True) + except Exception as e: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"❌ Ошибка подключения: {str(e)}\n") + logger.error(f"OpenVAS connection failed: {e}") + +def start_openvas_scan(scanner_service, session, scan_name, targets, config_name): + """Запуск OpenVAS сканирования""" + global openvas_scanner + if openvas_scanner is None: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + "❌ Не подключено к OpenVAS\n") + return + + try: + # Создание цели + target_id = openvas_scanner.create_target(scan_name, targets.split(',')) + # Создание задачи + task_id = openvas_scanner.create_task(scan_name, config_name, target_id) + # Запуск задачи + openvas_scanner.start_task(task_id) + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"🚀 Задача {task_id} запущена\n") + + # Сохранение сканирования в базе данных + scan = Scan( + session_id=session.id, + tool="openvas", + args=f"Имя: {scan_name}, Цели: {targets}, Конфигурация: {config_name}", + summary="В процессе", + created_at=datetime.utcnow() + ) + saved_scan = scanner_service.save_scan(scan) + if saved_scan: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"📝 ID сканирования: {saved_scan.id}\n") + + # Запуск потока для отслеживания статуса задачи + thread = threading.Thread( + target=monitor_openvas_task, + args=(scanner_service, session, saved_scan.id, task_id) + ) + thread.start() + + except Exception as e: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"❌ Ошибка: {str(e)}\n") + logger.error(f"OpenVAS scan failed: {e}") + +def monitor_openvas_task(scanner_service, session, scan_id, task_id): + """Отслеживание статуса задачи OpenVAS""" + global openvas_scanner + try: + start_time = time.time() + while True: + status = openvas_scanner.get_task_status(task_id) + if status == 'Done': + report = openvas_scanner.get_report(task_id) + if report: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + "✅ Сканирование завершено\n") + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"Отчет:\n{report}\n") + else: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + "❌ Отчет не найден\n") + break + elif status in ['Running', 'Requested']: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"⏳ Статус: {status}\n") + else: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"❌ Неизвестный статус: {status}\n") + break + time.sleep(10) + + # Обновление статуса сканирования в базе данных + scan = Scan( + id=scan_id, + summary="OpenVAS сканирование завершено", + duration=int(time.time() - start_time), + created_at=datetime.utcnow() + ) + scanner_service.update_scan(scan) + + if dpg.does_item_exist("scan_list"): + load_scan_history(scanner_service, session, "scan_list") + + except Exception as e: + dpg.set_value("openvas_output", dpg.get_value("openvas_output") + f"❌ Ошибка: {str(e)}\n") + logger.error(f"OpenVAS task monitoring failed: {e}") + def create_modbus_tab(scanner_service, session): """Вкладка Modbus сканирования""" with dpg.group():