🔍 Port Scanner + Banner Grabber: Пишем первый инструмент пентестера на Python
Автор: Mikhail Tarasov Timcore | Для новичков в этичном хакинге | Уровень: Beginner | Язык: Python 3.10+
⚠️ Дисклеймер: Всё описанное применяется исключительно на собственных системах или с письменного разрешения владельца. Несанкционированное сканирование чужих хостов -уголовная ответственность (УК РФ, ст. 272–274).
📖 Введение: зачем вообще сканировать порты?
Любой пентест начинается с разведки (Reconnaissance). Прежде чем искать уязвимости, нужно понять: что вообще работает на цели? Какие сервисы открыты? Какие версии ПО используются?
Именно здесь в игру вступает Port Scanner - инструмент, который:
- Определяет открытые TCP-порты
- Читает баннеры сервисов (версия, имя)
- Даёт тебе карту атаки ещё до первого payload'а
Мы напишем его на чистом Python, без сторонних библиотек -только стандартный socket. Запустил - работает везде.
🧱 Архитектура скрипта
Скрипт состоит из четырёх логических блоков:
[Ввод цели] → [DNS Resolve] → [TCP Scan] → [Banner Grab] → [Вывод]
Каждый блок - отдельная функция. Это важно: в реальных инструментах код должен быть модульным - легко дорабатывать и переиспользовать.
🎨 Блок 1 - Цвета в терминале
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
CYAN = "\033[96m"
RESET = "\033[0m"
BOLD = "\033[1m"
Это ANSI escape-коды - специальные последовательности символов, которые терминал интерпретирует как команды форматирования. Никаких библиотек, работает в любом Unix-терминале (Parrot, Kali, Ubuntu).
\033[- начало управляющей последовательности91m- красный цвет текста0m- сброс форматирования (обязательно в конце!)
Зачем это нужно? Когда сканируешь 1024 порта, важно моментально видеть [+] OPEN зелёным - глаза не должны парсить стену текста.
📚 Блок 2 - Словарь известных портов
KNOWN_PORTS = {
21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP",
53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP",
443: "HTTPS", 3306: "MySQL", 3389: "RDP", 8080: "HTTP-Alt",
8443: "HTTPS-Alt", 27017: "MongoDB", 6379: "Redis",
}Простой Python-словарь {порт: название}. При выводе результатов мы делаем KNOWN_PORTS.get(port, "Unknown") - если порт известен, показываем имя сервиса, иначе Unknown.
Лайфхак: Порты, которые сразу должны тебя насторожить:
23(Telnet) - plaintext как FTP, живой анахронизм3389(RDP) - под него тысячи брутфорс-ботов в интернете6379(Redis) - часто без пароля по умолчанию, RCE в подарок27017(MongoDB) - аналогично, бывает открыт всему миру
⚡ Блок 3 - Функция сканирования порта
def scan_port(ip: str, port: int, timeout: float = 0.5) -> bool:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
return s.connect_ex((ip, port)) == 0
except socket.error:
return False
Это сердце всего скрипта. Разберём по строкам:
|
Строка |
Что делает |
|
socket.AF_INET |
IPv4-адресация |
|
socket.SOCK_STREAM |
TCP-соединение (не UDP) |
|
s.settimeout(0.5) |
Ждём ответа максимум 0.5 сек |
|
connect_ex() |
Пытается установить TCP-соединение |
|
== 0 |
Если вернул 0 — порт открыт |
Почему connect_ex, а не connect? Потому что connect бросает исключение при ошибке -нам пришлось бы ловить try/except на каждый порт. connect_ex возвращает код ошибки: 0 = успех, всё остальное = порт закрыт/фильтруется.
Про таймаут: 0.5 секунды -баланс между скоростью и точностью. Меньше -можешь пропустить медленные сервисы. Больше - сканирование 1024 портов займёт вечность.
🎣 Блок 4 - Граббер баннеров
def grab_banner(ip: str, port: int, timeout: float = 2.0) -> str:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
if port in (80, 8080, 8443, 443):
s.send(b"HEAD / HTTP/1.0\r\nHost: target\r\n\r\n")
banner = s.recv(1024).decode(errors="ignore").strip()
return banner[:120]
except Exception:
return ""
Баннер - это то, что сервис говорит о себе при подключении. Именно он даёт версию ПО, а версия ПО → поиск CVE → профит.
Важный нюанс: HTTP-серверы не шлют баннер первыми - они ждут запроса. Поэтому для портов 80/443/8080 мы отправляем HEAD / запрос и читаем ответ с заголовками (Server: Apache/2.4.51, например).
💣 Код: port_scanner.py
#!/usr/bin/env python3
"""
[ Port Scanner + Banner Grabber ]
Этичный хакинг для новичков
⚠️ Используй ТОЛЬКО на своих системах или с письменным разрешением!
"""
import socket
import sys
from datetime import datetime
# === Цвета в терминале (без доп. библиотек) ===
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
CYAN = "\033[96m"
RESET = "\033[0m"
BOLD = "\033[1m"
# === Словарь: порт → имя сервиса ===
KNOWN_PORTS = {
21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP",
53: "DNS", 80: "HTTP", 110: "POP3", 143: "IMAP",
443: "HTTPS", 3306: "MySQL", 3389: "RDP", 8080: "HTTP-Alt",
8443: "HTTPS-Alt", 27017: "MongoDB", 6379: "Redis",
}
def grab_banner(ip: str, port: int, timeout: float = 2.0) -> str:
"""Пробуем прочитать баннер сервиса — версия, имя, иногда ОС."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
# Для HTTP-портов шлём GET-запрос, иначе просто слушаем
if port in (80, 8080, 8443, 443):
s.send(b"HEAD / HTTP/1.0\r\nHost: target\r\n\r\n")
banner = s.recv(1024).decode(errors="ignore").strip()
return banner[:120] # обрезаем, чтобы не флудило
except Exception:
return ""
def scan_port(ip: str, port: int, timeout: float = 0.5) -> bool:
"""Проверяем: порт открыт или нет. Возвращает True/False."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
# connect_ex возвращает 0 если порт открыт
return s.connect_ex((ip, port)) == 0
except socket.error:
return False
def run_scan(target: str, port_range: tuple[int, int]):
"""Основная функция сканирования."""
# Резолвим домен → IP
try:
ip = socket.gethostbyname(target)
except socket.gaierror:
print(f"{RED}[!] Не удалось резолвить хост: {target}{RESET}")
sys.exit(1)
start_port, end_port = port_range
open_ports = []
print(f"\n{BOLD}{CYAN}{'='*55}{RESET}")
print(f"{BOLD} 🎯 Цель : {ip} ({target}){RESET}")
print(f"{BOLD} 🔍 Порты : {start_port} – {end_port}{RESET}")
print(f"{BOLD} 🕐 Старт : {datetime.now().strftime('%H:%M:%S')}{RESET}")
print(f"{BOLD}{CYAN}{'='*55}{RESET}\n")
for port in range(start_port, end_port + 1):
if scan_port(ip, port):
service = KNOWN_PORTS.get(port, "Unknown")
banner = grab_banner(ip, port)
open_ports.append(port)
# Красивый вывод открытого порта
print(f" {GREEN}[+] OPEN{RESET} {BOLD}:{port:<6}{RESET} "
f"{YELLOW}{service:<12}{RESET}", end="")
if banner:
# Первая строка баннера — самое интересное
first_line = banner.splitlines()[0]
print(f" ↳ {CYAN}{first_line}{RESET}")
else:
print()
# Итог
print(f"\n{BOLD}{CYAN}{'='*55}{RESET}")
print(f" 📊 Открыто портов: {GREEN}{len(open_ports)}{RESET}")
if open_ports:
print(f" 🎰 Список: {', '.join(map(str, open_ports))}")
print(f"{BOLD}{CYAN}{'='*55}{RESET}\n")
# === Точка входа ===
if __name__ == "__main__":
print(f"{BOLD}{RED}")
print(" ╔══════════════════════════════════════╗")
print(" ║ ⚠️ ТОЛЬКО ДЛЯ ЭТИЧНОГО ХАКИНГА ║")
print(" ║ Используй с разрешения владельца! ║")
print(" ╚══════════════════════════════════════╝")
print(RESET)
target = input(" 🖥️ Введи IP или домен цели: ").strip()
try:
start = int(input(" 📌 Начальный порт [1]: ") or 1)
end = int(input(" 📌 Конечный порт [1024]: ") or 1024)
except ValueError:
print(f"{RED}[!] Некорректный ввод портов{RESET}")
sys.exit(1)
run_scan(target, (start, end))
🔍 Разбор по векторам
|
Этап |
Что происходит |
Зачем это нужно |
|
Resolve |
gethostbyname() |
Домен → IP, без этого никуда |
|
TCP Connect |
connect_ex() → 0 |
Самый простой и надёжный метод обнаружения открытых портов |
|
Banner Grab |
recv(1024) |
Узнаём версию сервиса — ищем CVE |
|
HTTP probe |
HEAD / |
HTTP-серверы не шлют баннер первыми, надо спросить |
🚀 Запуск и пример вывода
python3 port_scanner.py
🎯 Цель : 192.168.1.1
🔍 Порты : 1 – 1024
[+] OPEN :22 SSH ↳ SSH-2.0-OpenSSH_8.9p1 Ubuntu
[+] OPEN :80 HTTP ↳ HTTP/1.1 200 OK
[+] OPEN :443 HTTPS
📊 Открыто портов: 3
🎰 Список: 22, 80, 443
💡 Апгрейды для следующего шага
- Добавь
argparse- запуск из командной строки как у взрослых - Прикрути многопоточность (
threading) - сканить 1024 порта последовательно это больно - Интегрируй
python-nmap- для OS detection и версий сервисов - Натяни Burp Suite на HTTP-порты - и вот ты уже не новичок 😎
⚠️ Напоминалка от совести: Запускай только на своих стендах, виртуалках типа Metasploitable или на платформах вроде HackTheBox/TryHackMe. Сканирование чужих хостов без разрешения - это статья, не CTF. 🔐
