DISCLAIMER:
Решив использовать все описанные далее методы, предоставленный ниже код и перечисленные виды программного обеспечения, Вы должны предельно чётко понимать всю полноту юридической ответственности за их несанкционированное применение в условиях реального пентестинга и неукоснительно соблюдать требования Закона в этой области!
Пару недель назад я всерьёз открыл для себя новую “забаву” — использование локальных моделей ИИ для полуавтоматического “белого” пентестинга.
Мой исходный “суповой набор” из “железа” и софта.
-
Ноутбук Thinkpad P16 gen2 в комплектации i713850HX+ дискретная видеокарта Nvidia RTX 2000 ADA (8 Gb) + Samsung 990 Pto 1TB + Samsung 980 Pto 2TB + 4×32 Gb RAM (всего 128 Gb, но 32 Gb из них постоянно заняты под крайне необходимый в работе статический RAMDISK).
-
Приложение LM Studio в среде Windows 11 25H2 + текущая версия Python.
-
Портативный VirtualBox с множеством виртуальных машин, включая Kali Linux текущего релиза + Metasploitable 2 (тестовый учебный стенд для локальной отладки).
Любой опытный “иишник” сразу увидит в этом наборе одно “слабое звено” — не самая производительная дискретная видеокарта с крайне скромными 8Gb видеопамяти, пусть и аппаратно “заточенная” как раз на использование ИИ.
Тем не менее, быстрый процессор, два скоростных SSD и главное — большое количество оперативной памяти, вполне позволяют мне начать “ворочать” серьезными локальными моделями ИИ.
Две недели экспериментируя с разными локальными моделями, я, наконец, решил остановиться на очень тяжёлой модели Qwen 2.5 72B Instruct Q4_K_M размером в 44 гигабайта.
Почему именно на ней? Потому что с этой моделью на моём ноутбуке я могу часть слоёв модели направлять на супер быструю видеопамять, а всё оставшиеся слои — спокойно отправлять полностью в оперативную память, обеспечив тем самым терпимую скорость в токенах именно для целей пентестинга. Да, это “смешные” 2-3 токена в секунду, но мне же надо пентестить “по-белому”, а не “шашечки и поболтать”? Вполне хватает!
Кроме того, стоит не забывать, что это весьма умная модель, гораздо менее склонная к галлюцинированию при пентестинге, чем её более лёгкие “коллеги по цеху”. Ну, и с цензурой в командах у неё всё в порядке 😉
Итак, “железо” подготовлено, софт под рукой, но как же самым простым способом “научить” мою Windows на ноутбуке хотя бы в полуавтоматическом режиме “командовать” через локальную модель ИИ прямо внутри консоли в виртуальной машине Kali Linux?
Для этих целей не без помощи самого же ИИ я создал и отладил следующий “скрипт-мост” для Python исключительно под конкретную модель Qwen 2.5 72B Instruct Q4_K_M размером в 44 гигабайта. Особо отмечу, что скрипт сугубо экспериментальный, сырой, содержит много отладочного и прочего “мусора”, далёк от совершенства, но при этом — точно вполне рабочий!
Как использовать далее представленный “скрипт-мост” с указанной выше конкретной локальной моделью ИИ:
Готовим виртуальные машины и сеть между ними:
-
Полностью выключите виртуалку Kali в VirtualBox.
-
Откройте: “Настройки” Kali -> “Сеть” -> “Адаптер 1” (режим сети: NAT).
-
Нажмите “Дополнительно” -> “Проброс портов” -> Добавить новое правило:
-
Протокол: TCP
-
Порт хоста: 22222
-
Порт гостя: 22
-
-
Нажмите ОК, запустите Kali и откройте её терминал.
-
Активируйте SSH-сервер командой: sudo systemctl enable ssh —now
-
Разрешите беспарольный sudo для работы скрипта от root. Введите: sudo visudo
-
Прокрутите файл в самый низ и добавьте строку (где kali — имя вашего пользователя): kali ALL=(ALL) NOPASSWD: ALL
-
Сохраните и закройте файл (Ctrl+O -> Enter -> Ctrl+X). Проверьте командой “sudo whoami” (должно вернуть root без запроса пароля).
-
Дополнительно включите в этой же виртуалке Kali второй сетевой интерфейс в режиме “Внутренняя сеть” и настройте для него в Kali режим работы с адресацией для локальной сети между Kali и второй виртуальной машиной с Metasploitable2. Иными словами, один сетевой интерфейс Kali должен “видеть” внешнюю сеть, а второй — общается строго со второй виртуалкой с Metasploitable2 чисто по внутренней виртуальной сети. В моем случае это IP-адреса 192.168.5.4 и 192.168.5.3.
Установка рабочей среды Python (если ещё не установлен) и создание окружения на рамдиске или на вашем локальном диске.
-
Скачайте и запустите официальный инсталлер Python, например, “python-3.14.6-amd64.exe”.
-
В первом окне обязательно отметьте чекбокс “Add python.exe to PATH”. Нажмите “Install Now”.
-
Откройте стандартную командную строку Windows (cmd) от имени администратора.
-
Перейдите на ваш рамдиск W:, создайте изолированную папку для окружения и активируйте её:
W: md W:\AI_Bridge cd W:\AI_Bridge python -m venv env call env\Scripts\activate5. Обязательно установите проверенные официальные библиотеки следующей командой:
pip install paramiko openai
без которых “срипт-мост” просто не будет работать!
Настройка локального сервера LM Studio.
1. Запустите приложение LM Studio, загрузите в неё модель Qwen 2.5 72B Instruct Q4_K_M размером в 44 гигабайта и следом запустите её в LM Studio. Под свое “железо” настройте алгоритм загрузки слоев в видеопамять и в оперативную память в настройках модели в LM Studio.
2. Перейдите во вкладку “Developer” (левая панель) -> вкладка “Local Server”.
3. Нажмите зеленую кнопку “Start Server” (порт по умолчанию: 1234). Не закрывайте LM Studio.
Настройка “скрипта-моста” с автологированием.
Создайте в папке W:\AI_Bridge файл с именем “bridge.py” и вставьте следующий код (он автоматически сохраняет результаты в текстовый файл audit_log.txt прямо на вашем рамдиске):
import reimport osimport sysimport timeimport threadingimport msvcrtimport paramikofrom datetime import datetimefrom openai import OpenAI# === НАСТРОЙКИ СВЯЗКИ SSH И ФАЙЛОВ ===KALI_IP = "127.0.0.1" KALI_PORT = 22222 KALI_USER = "kali" KALI_PASS = "kali" LOG_FILE = r"W:\AI_Bridge\autonomous_audit_log.txt" MAX_HISTORY_TURNS = 10client = OpenAI( base_url="http://localhost:1234/v1", api_key="lm-studio", timeout=600.0)SSH_CONN = None SSH_CHAN = Nonedef log_to_file(text): """Логирование действий ИИ в файл""" timestamp = datetime.now().strftime("[%Y-%m-%d %H:%M:%S]") os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(f"{timestamp}\n{text}\n{'='*50}\n")def run_recv_thread(channel, stop_event): """Поток для непрерывного вывода данных из Kali в консоль Windows""" while not stop_event.is_set(): try: if channel.recv_ready(): data = channel.recv(1024).decode('utf-8', errors='ignore') if not data: stop_event.set() break sys.stdout.write(data) sys.stdout.flush() time.sleep(0.01) except Exception: stop_event.set() breakdef run_send_thread(channel, stop_event): """Поток для отправки нажатий клавиш из Windows в Kali""" while not stop_event.is_set(): try: line = sys.stdin.readline() if not line: break channel.send(line) except Exception: stop_event.set() breakdef interactive_shell(channel, manual_override=False): """ Потоковый интерактивный мост, подключающий клавиатуру к сокету Kali """ print("\n" + "="*60) if manual_override: print(" ?? РУЧНОЙ ПЕРЕХВАТ УПРАВЛЕНИЯ ОПЕРАТОРОМ (ПАУЗА ИИ) ?? ") else: print(" ?? КРИТИЧЕСКИЙ ПЕРЕХВАТ УПРАВЛЕНИЯ СИСТЕМОЙ (ЖИВОЙ КАНАЛ) ??") print("="*60) print("[УСПЕХ] Вы подключены напрямую к работающей консоли атаки!") print("[!] Для возврата управления ИИ-агенту введите 'exit' или нажмите Ctrl+C.\n") stop_event = threading.Event() t_recv = threading.Thread(target=run_recv_thread, args=(channel, stop_event), daemon=True) t_send = threading.Thread(target=run_send_thread, args=(channel, stop_event), daemon=True) t_recv.start() t_send.start() while not stop_event.is_set(): try: time.sleep(0.1) except KeyboardInterrupt: stop_event.set() break stop_event.set() print("\n[-] Возврат в автономный цикл ИИ...")def print_root_cheat_sheet(): """Вывод интерактивного меню подсказок оператору в момент пробива""" print("\n\n" + "!"*60) print(" ?? ЦЕЛЬ УСПЕШНО ПОРАЖЕНА! АВТОПИЛОТ ИИ ВСТАЛ НА ЖЕСТКИЙ СТОП-КАДР! ?? ") print("!"*60) print("\n СЛЕДУЙТЕ ЭТОЙ ИНСТРУКЦИИ, ЧТОБЫ ЗАБРАТЬ И СТАБИЛИЗИРОВАТЬ ШЕЛЛ:") print("?" * 60) print(" Шаг 1: Нажмите клавишу ENTER один раз, чтобы оживить сокет.") print(" Шаг 2: Введите команду проверки ваших текущих прав в сессии:") print(" whoami") print(" Шаг 3: Чтобы сделать слепой шелл интерактивным, введите:") print(" python -c \"import pty; pty.spawn('/bin/bash')\"") print(" Шаг 4: Убедитесь, что появился красивый промпт root@metasploitable:/#") print("?" * 60) print(" Нажмите Enter для активации живого канала клавиатуры...\n")def execute_ssh_cmd(command): global SSH_CONN, SSH_CHAN command = re.sub(r'^(msf\s*x?v?\d*|kali@\w+|root@\w+)\s*[>#\$]\s*', '', command, flags=re.IGNORECASE).strip() print(f"\n[*] [SSH] Отправка очищенной команды в живую сессию: {command}") log_to_file(f"[Автономный запуск команды]: {command}") if SSH_CONN is None or SSH_CHAN is None or SSH_CHAN.closed: try: SSH_CONN = paramiko.SSHClient() SSH_CONN.set_missing_host_key_policy(paramiko.AutoAddPolicy()) SSH_CONN.connect(KALI_IP, port=KALI_PORT, username=KALI_USER, password=KALI_PASS, timeout=30) SSH_CHAN = SSH_CONN.get_transport().open_session() SSH_CHAN.get_pty() SSH_CHAN.invoke_shell() SSH_CHAN.send("stty cols 1000 && export PAGER=cat\n") time.sleep(0.5) if SSH_CHAN.recv_ready(): SSH_CHAN.recv(4096) except Exception as e: return f"Ошибка инициализации SSH: {str(e)}", False try: SSH_CHAN.send(f"{command}\n") time.sleep(2.0) full_output = [] last_read_time = time.time() # ОРИГИНАЛЬНЫЙ ПЛАВАЮЩИЙ ЦИКЛ ЧТЕНИЯ ИЗ ВАШЕГО PDF while True: if SSH_CHAN.recv_ready(): data = SSH_CHAN.recv(4096).decode('utf-8', errors='ignore') if data: sys.stdout.write(data) sys.stdout.flush() full_output.append(data) last_read_time = time.time() if time.time() - last_read_time > 1.5: break time.sleep(0.05) combined_output = "".join(full_output) trigger_detected = False # ЖЕЛЕЗНЫЙ БЕСКОМПРОМИССНЫЙ ТРИГГЕР: ловит факт взлома и выводит Шпаргалку if re.search(r'(session \d+ opened|session opened|meterpreter >|command shell session|already interactive|uid=0\(root\))', combined_output, re.IGNORECASE): print_root_cheat_sheet() trigger_detected = True interactive_shell(SSH_CHAN, manual_override=False) return combined_output, trigger_detected except Exception as e: print(f"[-] КРИТИЧЕСКАЯ ОШИБКА SSH-МОСТА: {str(e)}") return f"Ошибка выполнения: {str(e)}", Falsedef main(): CURRENT_MODEL = "Qwen2.5-Coder-72B-Instruct" print("=== [AGENT ACTIVE] Запуск Автономного Движка (V8.0-AbsoluteAutonomy) ===") print("[!] Защита от обрезания строк, ложных вылетов из msf and пустых set-команд активна.") print("[*] Сброс окружения Kali Linux перед началом миссии...") try: temp_ssh = paramiko.SSHClient() temp_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) temp_ssh.connect(KALI_IP, port=KALI_PORT, username=KALI_USER, password=KALI_PASS, timeout=10) temp_ssh.exec_command("sudo killall -9 ruby msfconsole nmap nc telnet ftp") temp_ssh.close() print("[+] Окружение Kali полностью очищено.") except Exception as e: print(f"[!] Препреждение при очистке Kali: {e}") user_goal = input("\nСформулируйте задачу для агента: ") log_to_file(f"STARTING AUTONOMOUS MISSION: {user_goal}") full_system_instruction = ( "Ты — автономный ИИ-агент пентеста. Управляешь терминалом Kali Linux.\n" "Твоя цель — выполнить задачу пользователя и получить root-shell.\n\n" "ЖЕСТКИЕ ПРАВИЛА ВЗАИМОДЕЙСТВИЯ С ТЕРМИНАЛОМ:\n" "1. КАТЕГОРИЧЕСКОЕ ПРАВИЛО СКОРОСТИ NMAP: Тебе ЗАПРЕЩЕНО использовать медленные флаги '-p-' или '-A'! " "Для сканирования используй СТРОГО быстрый и легкий флаг: 'nmap -sV --open <target>'.\n" "2. Терминал удерживает контекст (как живой Bash). Все твои действия выполняются в одной непрерывной сессии.\n" "3. КАТЕГОРИЧЕСКИЙ ЗАПРЕТ НА ТОЧКУ С ЗАПЯТОЙ: Внутри msfconsole тебе строго запрещено разделять команды через ';'! " "Metasploit воспринимает ';' как часть имени модуля и выдает ошибку. Отправляй команды разделяя их обычной новой строкой.\n" "4. ПРАВИЛО ОБЯЗАТЕЛЬНОГО LHOST: Перед тем как написать команду 'exploit' или 'run' в Metasploit, ты ОБЯЗАНА на предыдущем шаге " "вручную выставить правильный локальный IP-адрес через команду 'set LHOST <IP_агента>'. Не полагайся на дефолтные значения Metasploit.\n" "5. КОНТЕКСТ ПРОМПТА: Всегда анализируй приглашение командной строки в логе. Если текущий промпт содержит 'msf >' или 'msf exploit', это значит, что ты НАХОДИШЬСЯ внутри msfconsole.\n" "6. Выдавай команды строго внутри блока кода ```bash ... ``` без комментариев." ) session_history = [ {"role": "system", "content": full_system_instruction}, {"role": "user", "content": f"Стартовая миссия: {user_goal}. Твой локальный IP для LHOST: {KALI_IP if KALI_IP != '127.0.0.1' else '192.168.5.4'}"} ] step_counter = 1 last_terminal_prompt = "" try: while True: if msvcrt.kbhit(): key = msvcrt.getch().decode('utf-8', errors='ignore').lower() if key == 'p': print("\n[!] ОБНАРУЖЕН СИГНАЛ РУЧНОЙ ПАУЗЫ ИИ ОТ ОПЕРАТОРА.") global SSH_CHAN if SSH_CHAN and not SSH_CHAN.closed: interactive_shell(SSH_CHAN, manual_override=True) print("[+] Пауза снята. Передаю управление обратно ИИ-агенту...") print(f"\n Начинаю шаг вычислений #{step_counter}...") if len(session_history) > (MAX_HISTORY_TURNS * 2) + 2: del session_history[2:4] try: completion = client.chat.completions.create(model=CURRENT_MODEL, messages=session_history, temperature=0.0) except Exception as e: print(f"[-] Ошибка обращения к API: {e}") break try: # РОДНОЙ ВАРИАНТ ПАРСИНГА ИЗ ВАШЕГО PDF if hasattr(completion, 'choices') and len(completion.choices) > 0: first_choice = completion.choices[0] if hasattr(first_choice, 'message') and hasattr(first_choice.message, 'content'): response = first_choice.message.content elif isinstance(first_choice, dict) and 'message' in first_choice: response = first_choice['message']['content'] else: response = getattr(first_choice, 'text', '') else: data = completion.model_dump() if hasattr(completion, 'model_dump') else dict(completion) response = data['choices'][0]['message']['content'] except Exception as e: print(f"[-] Ошибка парсинга ответа API: {e}") break clean_response = response.strip() print(f"\n[Ответ ИИ-Ассистента]:\n{clean_response}") cmd_to_run = "" try: # РОДНОЙ ВАРИАНТ ПАРСИНГА ИЗ ВАШЕГО PDF БЕЗ ИЗМЕНЕНИЙ commands = re.findall(r'```(?:bash|sh)?\s*(.*?)\s*```', clean_response, re.DOTALL) if not commands: if any(k in clean_response for k in ["nmap", "set ", "use ", "exploit", "msfconsole", "exit"]): lines = [l.strip() for l in clean_response.split('\n') if l.strip()] if lines: cmd_to_run = "\n".join(lines) else: print(f"[-] ИИ прислал обычный текст на шаге {step_counter}.") session_history.append({"role": "assistant", "content": response}) session_history.append({"role": "user", "content": "Ошибка: Пришли конкретную bash-команду в формате ```bash ... ```"}) step_counter += 1 continue else: first_block = commands[0] target_lines = [line.strip() for line in first_block.split('\n') if line.strip() and not line.strip().startswith('#')] if target_lines: cmd_to_run = "\n".join(target_lines) except Exception as parse_err: print(f"[-] Внутренняя ошибка разбора строки: {parse_err}. Восстановление шага.") cmd_to_run = "" if not cmd_to_run: print("[-] Валидных команд не обнаружено. Повторный запрос.") session_history.append({"role": "user", "content": "Ошибка выполнения разметки. Повтори команду корректно."}) continue # --- ИНТЕЛЛЕКТУАЛЬНЫЙ ПЕРЕХВАТ ОШИБОК КОНТЕКСТА --- first_line_check = cmd_to_run.split('\n')[0].strip() if "set " in first_line_check and "msf >" in last_terminal_prompt and not "exploit(" in last_terminal_prompt: print("[!] Перехват ошибки датастора: ИИ шлет 'set' в корневом меню. Корректирую фидбек.") user_feedback = "Внимание: Ты находишься в корневом меню msf >. Твоя команда set была проигнорирована. Сначала выбери эксплойт через команду 'use <путь_к_модулю>'!" session_history.append({"role": "assistant", "content": response}) session_history.append({"role": "user", "content": user_feedback}) step_counter += 1 continue is_actually_in_msf = "msf >" in last_terminal_prompt or "msf exploit(" in last_terminal_prompt or "msf auxiliary(" in last_terminal_prompt if "msfconsole" in first_line_check and is_actually_in_msf: print(f"[!] Перехват рекурсии: ИИ вызывает msfconsole внутри msfconsole.") user_feedback = "Внимание: Ты уже находишься внутри msfconsole! Не пиши 'msfconsole'. Используй чистые команды 'use' или 'set'." session_history.append({"role": "assistant", "content": response}) session_history.append({"role": "user", "content": user_feedback}) step_counter += 1 continue if any(k in first_line_check for k in ["set ", "use ", "exploit"]) and not is_actually_in_msf and "kali@kali" in last_terminal_prompt: print("[!] Настоящий вылет из msf: Автоматически восстанавливаю консоль Metasploit.") cmd_to_run = f"msfconsole -q -x '{cmd_to_run}'" if "nmap" in first_line_check and is_actually_in_msf: print("[!] Модель шлет nmap внутрь msfconsole. Принудительный выход в Bash.") cmd_to_run = "exit" # ----------------------------------------------------------------- print(f"\n[Агент отправляет]: {cmd_to_run}") kali_output, root_pwned = execute_ssh_cmd(cmd_to_run) if root_pwned: print("\n[*] Ручной перехват завершен. Контроллер удерживает окно.") break last_terminal_prompt = kali_output if not kali_output or len(kali_output.strip()) == 0: user_feedback = f"Результат выполнения команды '{cmd_to_run}' пустой. Продолжай аудит хоста." else: user_feedback = f"Результат выполнения твоей команды '{cmd_to_run}':\n```text\n{kali_output}\n```\nПроанализируй этот вывод терминала, обрати внимание на промпт командной строки и пришли СЛЕДУЮЩУЮ команду." session_history.append({"role": "assistant", "content": response}) session_history.append({"role": "user", "content": user_feedback}) step_counter += 1 print("-" * 40) except KeyboardInterrupt: print("\n[-] Работа агента экстренно остановлена оператором.") print("\n" + "="*60) print("[КОНЕЦ] Основной цикл завершен. Окно удерживается скриптом.") print("Нажмите Enter для окончательного выхода...") input()if __name__ == "__main__": main()Как запускать всю “связку”?
Каждый раз после перезагрузки компьютера или ноутбука (так как рамдиск очищается), при заранее запущенной LM Studio с подгруженной в режиме локального сервера моделью, для воссоздания окружения и запуска просто откройте cmd и выполните:
W:cd W:\AI_Bridgeif not exist env ( python -m venv env call env\Scripts\activate pip install paramiko openai) else ( call env\Scripts\activate)python bridge.pyИли для удобства вообще создайте BAT-файл с указанными выше командами для быстрого старта.
Поддержание созданной рабочей среды в локальном безопасном состоянии.
Для периодического обновления установленных библиотек до последних безопасных версий откройте cmd на Windows и выполните:
W:cd W:\AI_Bridgecall env\Scripts\activatepip install --upgrade paramiko openaiИ сохраните новый “бэкап” обновленной папки.
Что же дальше?
А дальше запускаем обе виртуалки — Kali Linux и Metasplotable 2, запускаем “скрипт-мост” и прямо в консоли создаем первую задачу:
“Проведи аудит хоста 192.168.5.3 (Metasploitable 2) с нуля до root-shell. Начни с nmap. Твой IP — 192.168.5.4”
На моем ноутбуке под эту задачу примерно за 2-3 минуты локальная модель сама просканит виртуалку с Metasploitable 2 и разными способами (в зависимости от коррекции постановки задачи) получит root-shell, встав на паузу и передав управление оператору (мне). И дополнительно выдаст подсказки. Также работу модели через “скрипт-мост” можно прервать через Ctrl+С или же встать на паузу через нажатие клавиши “P”, перейдя в ручное управление.
Конечно, виртуалка с Metasploitable2 — это чисто “тепличная” учебная среда, весьма далекая от реальных задач с реальными хостами. Но ведь я лишь только учусь и безопасно экспериментирую, “прощупывая” возможности ИИ для этичного “белого” пентестинга.
Очень многое в описанном сценарии можно усовершенствовать, очень многое пока недоработано. Модель всё еще запросто может галлюцинировать, но это только начало весьма интересного проекта. Думаю всё описанное выше может стать полезной “отправной точкой” для многих других энтузиастов в сфере ИИ и “белого” пентестинга.
Удачи в Ваших разумных и законных экспериментах!
ссылка на оригинал статьи https://habr.com/ru/articles/1055728/