🔍 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. 🔐

8 views·1 share