Industrial Network Scanner: как мы написали на Go инструмент аудита безопасности ICS/SCADA-сетей
- пятница, 5 июня 2026 г. в 00:00:11
Привет, Хабр! Хочу рассказать об инструменте, который мы делали для реальных задач — аудита безопасности промышленных сетей. Называется Industrial Scanner Pro, написан на Go, имеет веб-интерфейс и умеет работать с тремя ключевыми промышленными протоколами. Репозиторий лежит на GitVerse.
Статья будет полезна тем, кто занимается ОТ-безопасностью, пишет инструменты для пентеста промышленных систем или просто интересуется, как устроены ICS-сети изнутри.
Когда берёшься за аудит промышленной сети, инструментарий оказывается неожиданно скудным. Nmap умеет многое, но не понимает Modbus или S7. Специализированные коммерческие решения стоят десятки тысяч долларов и заточены под конкретных вендоров. Открытые альтернативы — PLCscan, modscan, s7scan — узкоспециализированы: каждый работает с одним протоколом, у большинства нет нормального UI, а агрегировать результаты приходится руками.
Нам нужно было:
Один инструмент для трёх основных промышленных протоколов
Оценка рисков по каждому обнаруженному устройству, а не просто список открытых портов
Веб-интерфейс — чтобы можно было показать заказчику прямо в браузере, без установки клиента
Кросс-платформенность — собирается под Linux, Windows и macOS из одного codebase
Так появился Industrial Scanner Pro.
Самый распространённый промышленный протокол. Появился в 1979 году и до сих пор живёт в большинстве ПЛК, частотных преобразователях и промышленных контроллерах. Главная его проблема с точки зрения безопасности — полное отсутствие аутентификации. Любой, кто добрался до сети, может читать и писать регистры.
Сканер подключается к устройству, читает Holding Registers и Input Registers, опрашивает несколько Unit ID и строит карту доступных данных.
[Modbus] 192.168.1.10:502 Unit ID: 1 — доступен Holding Registers [0–9]: [1024, 0, 255, 0, 512, ...] Coils [0–7]: [1, 0, 1, 1, 0, 0, 1, 0] Риск: ВЫСОКИЙ — открытая запись регистров без аутентификации
Протокол семейства SIMATIC S7 — де-факто стандарт для европейской промышленности. Работает поверх ISO-on-TCP (RFC 1006). Содержит богатую диагностическую информацию: имя устройства, версию прошивки, серийный номер, тип CPU.
Именно эти данные особенно ценны при разведке: зная точную версию прошивки Siemens S7-300, можно проверить известные CVE.
[S7] 192.168.1.20:102 Имя: S7-300 CPU 315-2 DP Версия: V3.3.12 Серийный номер: S C-X4U487474 Тип модуля: CPU Риск: СРЕДНИЙ — доступна диагностическая информация
Протокол, широко используемый в электроэнергетике, водоснабжении и нефтегазовой отрасли. Поддерживает метки времени, CRC-проверку и адресацию удалённых терминальных устройств (RTU). В отличие от Modbus, чуть сложнее в реализации, но принципиально та же история: в большинстве инсталляций аутентификация отсутствует или опциональна.
Проект разбит на чёткие слои:
industrial-network-scanner/ ├── main.go # Точка входа, HTTP-сервер, маршрутизация ├── scanner/ # Ядро сканирования │ ├── scanner.go # Оркестратор: управление горутинами, агрегация │ ├── modbus.go # Modbus TCP клиент │ ├── s7.go # Siemens S7 клиент (ISO-on-TCP) │ └── dnp3.go # DNP3 клиент ├── frontend/ # Финальная сборка веб-интерфейса ├── frontend_modern/ # Исходники UI (современная версия) ├── frontend_legacy/ # Исходники UI (легаси версия) ├── go.mod ├── build.sh # Сборка под Linux/macOS └── build_windows.bat # Сборка под Windows
Три причины:
Горутины. Сканирование сети — задача с высоким параллелизмом. На /24-подсети это 254 хоста × 3 протокола = 762 потенциальных соединения. Go позволяет запускать их конкурентно без головной боли с thread pools.
Статическая компиляция. Бинарник не тянет зависимостей. Скопировал на машину — запустил. Для полевого аудита это критично.
Стандартная библиотека. Встроенный net/http позволяет поднять веб-сервер буквально в десяток строк. Не нужен ни Flask, ни Node — сканер сам раздаёт свой фронтенд.
// Упрощённая схема оркестратора func (s *Scanner) Scan(targets []string) []Result { results := make(chan Result, len(targets)*3) var wg sync.WaitGroup for _, target := range targets { wg.Add(3) go func(t string) { defer wg.Done() results <- s.scanModbus(t) }(target) go func(t string) { defer wg.Done() results <- s.scanS7(t) }(target) go func(t string) { defer wg.Done() results <- s.scanDNP3(t) }(target) } wg.Wait() close(results) // агрегация и оценка рисков... }
Каждый протокольный модуль независим: у него своя логика подключения, таймауты и разбор ответа.
Это, пожалуй, самая интересная часть. Мало обнаружить устройство — важно объяснить, насколько оно опасно.
Модель оценки строится из нескольких факторов:
Фактор | Описание |
|---|---|
Открытость протокола | Доступен ли протокол без аутентификации |
Объём раскрытых данных | Количество читаемых регистров / тип раскрытой информации |
Возможность записи | Можно ли не только читать, но и изменять данные |
Идентификация устройства | Доступны ли версия прошивки, серийный номер, тип CPU |
Критичность сегмента | Энергетика, водоснабжение, производство — разный вес |
Итоговый уровень: LOW / MEDIUM / HIGH / CRITICAL.
Устройство: 192.168.1.10 Протоколы: Modbus TCP, DNP3 Открытая запись: ДА (Modbus holding registers) Раскрытие версии: НЕТ Итоговый риск: CRITICAL Рекомендации: - Ограничить доступ к порту 502 с помощью промышленного firewall - Внедрить Modbus-proxy с контролем команд - Рассмотреть перевод на Modbus Security (RFC 2217 + TLS)
Сканер умеет принимать цели в нескольких форматах:
# Одиночный хост ./scanner 192.168.1.100 # CIDR-диапазон ./scanner 192.168.1.0/24 # Список из файла ./scanner -f targets.txt # Диапазон адресов ./scanner 10.0.0.1-10.0.0.50
Статический фронтенд раздаётся прямо из бинарника — файлы встраиваются через //go:embed. Открываешь браузер на http://localhost:8080, и получаешь дашборд с:
картой обнаруженных устройств
детальными данными по каждому узлу
цветовой маркировкой уровней риска
возможностью экспорта в JSON и CSV
В репозитории есть две версии фронтенда: frontend_modern (актуальная) и frontend_legacy (совместимость со старыми браузерами промышленных HMI-станций — там до сих пор встречается IE11).
Помимо UI, сканер предоставляет API для интеграции с другими системами:
GET /api/scan?target=192.168.1.0/24 — запустить сканирование GET /api/results — получить результаты GET /api/results/{ip} — результаты по конкретному хосту GET /api/export?format=json — экспорт GET /api/export?format=csv — экспорт в CSV
Это позволяет встраивать сканер в пайплайны CI/CD безопасности или в SIEM-системы.
# Linux / macOS git clone https://gitverse.ru/plcstudio/industrial-network-scanner cd industrial-network-scanner ./build.sh # Windows build_windows.bat # Или вручную go build -o industrial-scanner main.go
Требования: Go 1.21+. Внешних зависимостей минимум — всё описано в go.mod.
Для сборки под все платформы сразу:
GOOS=linux GOARCH=amd64 go build -o dist/scanner-linux-amd64 main.go GOOS=windows GOARCH=amd64 go build -o dist/scanner-windows-amd64.exe main.go GOOS=darwin GOARCH=arm64 go build -o dist/scanner-darwin-arm64 main.go
Инструмент предназначен исключительно для авторизованного аудита безопасности. Сканирование промышленных сетей без разрешения владельца — уголовно наказуемое деяние во многих юрисдикциях, включая Россию (ст. 272, 273 УК РФ).
Всегда получайте письменное разрешение перед использованием.
В планах:
Поддержка EtherNet/IP (Rockwell/Allen-Bradley) — протокол широко используется в американских промышленных установках
Поддержка OPC-UA — современный стандарт, который активно вытесняет старые протоколы
Пассивный режим — обнаружение устройств через анализ трафика без активного сканирования (более безопасно для хрупких OT-устройств)
Интеграция с базой CVE для автоматического поиска известных уязвимостей по обнаруженным версиям прошивок
Industrial Scanner Pro — это попытка сделать адекватный open‑source инструмент для аудита ICS‑сетей: с поддержкой основных протоколов, оценкой рисков, нормальным UI и возможностью встраивания в автоматизированные пайплайны.
Код открыт, issues и PR приветствуются. Если занимаетесь ОТ‑безопасностью или просто интересуетесь темой — буду рад обратной связи в комментариях.