Сергей Попов
Администратор
- 30.12.2015
- 6 221
- 6 963
- Специализация
- OSINT
- Веб-безопасность
- Статус верификации
- ✓ Verified
На внутреннем пентесте в прошлом квартале я оказался на захваченном Linux-хосте с Python 3.9 и стандартной библиотекой - ни nmap, ни masscan, ни возможности доставить бинарь без детекта EDR. За два часа написал асинхронный сканер, который обошёл /24-подсеть, собрал баннеры с открытых портов и нашёл непатченный Jenkins с anonymous access. Metasploit на хосте не стоял. Python решил задачу.
Русскоязычные гайды по python для пентестера обычно показывают последовательный сканер через
socket.connect_ex() и перебор паролей в цикле for pwd in passwords без паузы. Ни OPSEC, ни обработки ошибок, ни привязки к реальному kill chain. Для лабораторной хватит. Для проекта, где на хосте стоит CrowdStrike Falcon и в сети крутится Elastic SIEM - нет.Ниже - python пентест скрипты, которые я использую на реальных проектах. Каждый привязан к конкретному этапу MITRE ATT&CK, содержит обработку ошибок и учитывает ограничения боевой среды.
Место кастомных скриптов в цепочке атаки
Кастомный Python-код покрывает несколько тактик MITRE ATT&CK. Без понимания, на каком шаге kill chain он применяется, скрипт - просто файл на диске. Вот где встаёт каждый тип скрипта:- Reconnaissance - Active Scanning (T1595): сканирование IP-блоков (T1595.001), поиск уязвимых сервисов (T1595.002). Первый шаг: понять, что живёт в сети.
- Discovery - Network Service Discovery (T1046): после получения foothold собираем баннеры и определяем версии сервисов во внутренней сети.
- Credential Access - Brute Force (T1110): подбор паролей к найденным сервисам - SSH, HTTP-формы, SMB.
- Initial Access - Exploit Public-Facing Application (T1190): кастомный exploit под конкретную CVE, когда готового модуля нет.
- Execution - Python (T1059.006): сам факт выполнения скрипта на скомпрометированном хосте.
Разница с фреймворками тут принципиальная. Metasploit оставляет характерные артефакты - user-agent
Mozilla/4.0 (compatible; MSIE 6.0), паттерны сканирования, сигнатуры exploit-модулей в логах. Кастомный python скрипт для разведки контролируется полностью: заголовки, тайминги, порядок запросов. На проекте с CrowdStrike Falcon или мониторингом через Elastic 8.x+ это разница между детектом и тишиной. Доставка бинаря (Ingress Tool Transfer, T1105) триггерит детект - а Python-интерпретатор уже стоит на хосте.Требования к окружению
Перед запуском скриптов - минимальный чеклист:- ОС: Kali Linux 2024.x, Parrot OS, Ubuntu 22.04+ или любой дистрибутив с Python 3.9+ (нужен
asyncio.open_connection) - RAM: 2 ГБ минимум для async-сканера на /24, 4 ГБ при параллельном сборе баннеров с сотен хостов
- Python: 3.9+ (stdlib:
asyncio,socket,ssl,struct,re) - Дополнительные пакеты (при наличии pip):
aiohttpдля HTTP-brute-force,requestsдля одиночных запросов; для сырых пакетов -scapy - Сеть: скрипты работают локально (сокеты к целям), внешний доступ не нужен
- Права: root не нужен для TCP connect-сканирования; для SYN-сканирования через raw sockets или Scapy нужен root или
CAP_NET_RAW
Асинхронное сканирование портов через Python-сокеты
Последовательный сканер из учебников работает так:socket.connect_ex() по каждому порту, таймаут 1 секунда. 1024 порта на одном хосте - 17 минут. На десяти хостах - почти три часа. На внутреннем пентесте, где за ночь нужно покрыть подсеть, это неприемлемо.Автоматизация пентеста python через
asyncio решает проблему: asyncio.open_connection() проверяет порт неблокирующим вызовом, а Semaphore ограничивает количество одновременных соединений - чтобы не убить сеть и не сожрать файловые дескрипторы.
Python:
import asyncio
async def check(sem, host, port, t=0.8):
async with sem:
try:
_, w = await asyncio.wait_for(asyncio.open_connection(host, port), t)
w.close(); await w.wait_closed(); return port
except Exception: return None
async def scan(host, ports, conc=200):
sem = asyncio.Semaphore(conc)
res = await asyncio.gather(*(check(sem, host, p) for p in ports))
return sorted(p for p in res if p)
asyncio.run(scan("10.10.14.5", range(1, 1025))) - 1024 порта за 5-8 секунд на живом хосте. Параметр conc контролирует нагрузку: на внутреннем пентесте ставлю 200-500, на внешнем - 30-80, чтобы не триггерить IDS.Предусловия и ограничения:
- Работает если: цель отвечает RST на закрытые порты (стандартное поведение TCP-стека). Не работает если: хост за stateful firewall, который дропает пакеты молча - таймаут на каждый закрытый порт убивает скорость, и преимущество async теряется.
- Full TCP connect оставляет записи в логах сервисов - это не SYN-scan. Для stealth-сканирования нужен Scapy с SYN-пакетами (
IP(dst=host)/TCP(dport=port, flags='S')) и root-права. - На подсетях /16 и больше async-сканер может исчерпать лимит файловых дескрипторов. Дефолт в Linux - 1024. Перед запуском:
ulimit -n 65535. - [Внутренний пентест] - основной сценарий. На внешнем предпочтительнее masscan с контролируемого VPS: он на порядок быстрее благодаря собственному сетевому стеку.
Сбор баннеров и fingerprinting: python скрипты для разведки сервисов
Открытый порт без контекста - бесполезная строка в отчёте. Следующий шаг - Network Service Discovery (T1046): определить, что на порту работает, какой версии, с какой конфигурацией.Принцип простой: подключаемся к порту, отправляем probe (или ждём баннер), парсим ответ. Для HTTP шлём
HEAD / HTTP/1.0\r\n\r\n, для SSH и SMTP - просто читаем первую строку: сервис сам отдаёт greeting.
Python:
async def grab(host, port, timeout=3):
probes = {80: b"HEAD / HTTP/1.0\r\nHost: t\r\n\r\n", 22: b"", 25: b""}
try:
r, w = await asyncio.wait_for(asyncio.open_connection(host, port), timeout)
probe = probes.get(port, b"")
if probe: w.write(probe)
data = await asyncio.wait_for(r.read(1024), timeout)
w.close(); return port, data.decode(errors="replace").strip()
except Exception: return port, ""
(порт, баннер). Парсинг через re.search() даёт конкретные версии: SSH-2.0-OpenSSH_8.9p1, Apache/2.4.52, 220 mail.target.local ESMTP Postfix. Из версий уже можно искать CVE в NVD по CPE-идентификатору или на exploit-db.Chaining с предыдущим шагом: результат
scan() передаётся напрямую - для каждого открытого порта запускается grab(). Весь пайплайн укладывается в десять строк: сканирование → баннеры → парсинг версий.OPSEC-нюанс: HTTP-запрос
HEAD / оставляет запись в access-логе веб-сервера. На проектах с мониторингом через Elastic SIEM или Splunk меняйте User-Agent на легитимный. Парадоксально, но python-requests/2.31.0 вызывает меньше подозрений в корпоративной среде, чем curl/7.88.1 - разработчики активно используют Python, и такой UA выглядит как внутренний скрипт. На портах SSH, FTP, SMTP probe не нужен - достаточно пассивного чтения баннера.Брутфорс с OPSEC: автоматизация подбора паролей на Python
Подбор паролей - Brute Force (T1110, Credential Access). Python для хакеров часто ассоциируется именно с этой задачей, но скрипты из учебников игнорируют три проблемы реальной среды: rate-limiting, lockout policies и корреляцию в SIEM.Вот что отличает рабочий brute-force скрипт от учебного:
Jitter вместо фиксированного sleep.
time.sleep(1) - это маяк для SIEM. await asyncio.sleep(random.uniform(0.5, 2.0)) ломает паттерн равномерных запросов, который корреляционные правила детектят по аномалии интервалов.Lockout detection. Три ответа подряд с HTTP 429 (Too Many Requests), 403 или изменённым содержимым страницы (появилась CAPTCHA) - скрипт уходит на паузу 30-60 секунд. Без этого на AD-доменной аутентификации через веб-форму пять неудачных попыток блокируют учётку на 30 минут. На пентесте это означает сломанный production. Объясняй потом заказчику.
Exponential backoff при сетевых ошибках. Connection reset, таймаут - повтор до 3 раз с нарастающей задержкой (1с, 2с, 4с). Без retry скрипт молча пропускает валидный пароль.
Ротация User-Agent. Меняем заголовок каждые 10-20 запросов из списка реальных браузеров. Не панацея, но усложняет корреляцию в логах WAF.
Реализация на
aiohttp с asyncio.Semaphore(5) для ограничения параллелизма: пять одновременных попыток - достаточно для скорости и недостаточно для срабатывания rate-limiter большинства веб-приложений. Проверка успешного входа - по наличию специфического элемента в response.text: cookie, redirect URL, уникальная строка на странице после авторизации.Предусловия и ограничения:
- Работает если: аутентификация через HTTP-форму без CAPTCHA. Если CSRF-токен присутствует - его нужно дёргать перед каждым запросом через парсинг HTML (
re.search(r'name="csrf" value="(.*?)"', page)) и вставлять в POST-данные. - Не работает если: цель использует MFA, client-side JavaScript-challenge, или WAF с browser fingerprinting (Cloudflare Under Attack Mode, Akamai Bot Manager).
- [Внешний пентест] - только с явного разрешения заказчика, из белого списка IP. [Внутренний] - lockout detection обязателен.
- Против AD/Kerberos-аутентификации HTTP-брут через веб-форму неэффективен - лучше
kerbruteили кастомный скрипт через Kerberos pre-authentication (другая механика, другие артефакты).
Автоматизация разведки: пайплайн от подсети до CVE
Отдельные скрипты для эксплуатации уязвимостей python - штука полезная, но реальная ценность в цепочке. Один скрипт сканирует подсеть, второй собирает баннеры, третий парсит версии и ищет CVE. Объединяем в pipeline - получается автоматизация OSINT python на внутреннем уровне.Типичная цепочка на внутреннем пентесте:
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Запуск одной командой:
python3 recon_chain.py --subnet 10.10.14.0/24 --ports 1-1024 --output results.json. На выходе - JSON-дерево, из которого сразу видно: на каком хосте какой сервис, какой версии, какие CVE потенциально применимы.OPSEC на внутреннем пентесте с SIEM. Массовое python сетевое сканирование /24 с одного хоста генерирует алерты по Network Service Discovery (T1046). MaxPatrol SIEM, Elastic Security 8.x+ и Splunk ES имеют готовые корреляционные правила на это поведение. Контрмера: разбивайте сканирование на пачки по 10-20 хостов с паузами 30-60 секунд между пачками. Это снижает вероятность срабатывания правила, которое считает количество уникальных destination IP за временное окно. (На одном проекте с MaxPatrol SIEM я сканировал по 15 хостов с паузой 45 секунд - ни одного алерта за ночь.)
Когда Python-скрипт, а когда фреймворк
Кастомный код - не серебряная пуля. Python библиотеки для пентестеров (scapy, impacket, aiohttp) хороши, но написание эксплойтов на python с нуля оправдано не всегда.| Задача | Кастомный Python | Готовый фреймворк | Когда кастом оправдан |
|---|---|---|---|
| Сканирование портов | asyncio + socket | nmap / masscan | На захваченном хосте без nmap; нужен контроль таймингов для stealth |
| Брутфорс HTTP | aiohttp + jitter | Hydra / Burp Intruder | Нестандартная логика аутентификации: CSRF, multi-step, JSON API |
| SQL-инъекции | requests + ручной payload | sqlmap | Blind SQLi с нетипичным ответом; WAF-bypass с кастомным encoding |
| Эксплуатация CVE | socket / requests | Metasploit module | Нет готового модуля в MSF; нужно избежать сигнатуры Metasploit |
| ARP spoofing | Scapy | Bettercap | Редко оправдан - Bettercap удобнее; Python разве что на embedded-устройствах |
| SMB enum | impacket | NetExec | Кастомная фильтрация share по содержимому файлов |
Decision tree:
- Готовый инструмент есть на хосте → используй его.
- Инструмента нет, но можно доставить бинарь без детекта → доставь (nmap-static, chisel).
- Доставка рискованна (EDR детектит drop, нет канала) → пиши на Python.
- Задача нестандартная (кастомная логика, нетипичный протокол) → пиши на Python в любом случае.
Собственная библиотека утилит - то, к чему стоит прийти после нескольких проектов. Модуль async-сканирования, модуль banner parsing, модуль OPSEC-brute-force - каждый по 100-200 строк. Вместе они экономят часы. А главное - не приходится каждый раз вспоминать, зачем в прошлом скрипте стоял
sleep(0.7) и почему парсер ломался на HTTP/2-ответах. Документируйте свои скрипты так, будто через полгода их будет читать злой и невыспавшийся вы. Потому что так и будет.Я вижу тренд: заказчики разворачивают EDR агрессивнее, SIEM-правила становятся точнее, и окно для запуска стандартных инструментов сужается. Через год-два умение написать чистый async-сканер на stdlib будет не бонусом в резюме, а базовым требованием. Не потому что nmap плох - он отличный. А потому что доставить его на целевой хост незамеченным уже сейчас получается не на каждом проекте.
Те, кто привык полагаться только на фреймворки, рискуют застрять на этапе, где нужен один скрипт на 50 строк - и не смогут его написать. Попробуйте взять async-сканер из этой статьи и прогнать его по своей лабе. Если нашли открытый 8080 или 3306 без авторизации - поздравляю, у вас та самая проблема, которую Jenkins-администраторы не замечают годами.
На курсе WAPT эту цепочку - от сканера на stdlib до кастомного exploit - проходят в течение нескольких модулей с лабами.