golang

Industrial Network Scanner: как мы написали на Go инструмент аудита безопасности ICS/SCADA-сетей

  • пятница, 5 июня 2026 г. в 00:00:11
https://habr.com/ru/articles/1043682/

Привет, Хабр! Хочу рассказать об инструменте, который мы делали для реальных задач — аудита безопасности промышленных сетей. Называется Industrial Scanner Pro, написан на Go, имеет веб-интерфейс и умеет работать с тремя ключевыми промышленными протоколами. Репозиторий лежит на GitVerse.

Статья будет полезна тем, кто занимается ОТ-безопасностью, пишет инструменты для пентеста промышленных систем или просто интересуется, как устроены ICS-сети изнутри.

Зачем ещё один сканер?

Когда берёшься за аудит промышленной сети, инструментарий оказывается неожиданно скудным. Nmap умеет многое, но не понимает Modbus или S7. Специализированные коммерческие решения стоят десятки тысяч долларов и заточены под конкретных вендоров. Открытые альтернативы — PLCscan, modscan, s7scan — узкоспециализированы: каждый работает с одним протоколом, у большинства нет нормального UI, а агрегировать результаты приходится руками.

Нам нужно было:

  • Один инструмент для трёх основных промышленных протоколов

  • Оценка рисков по каждому обнаруженному устройству, а не просто список открытых портов

  • Веб-интерфейс — чтобы можно было показать заказчику прямо в браузере, без установки клиента

  • Кросс-платформенность — собирается под Linux, Windows и macOS из одного codebase

Так появился Industrial Scanner Pro.

Поддерживаемые протоколы

Modbus TCP (порт 502)

Самый распространённый промышленный протокол. Появился в 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]
  Риск: ВЫСОКИЙ — открытая запись регистров без аутентификации

Siemens S7 (порт 102)

Протокол семейства 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
  Риск: СРЕДНИЙ — доступна диагностическая информация

DNP3 (порт 20000)

Протокол, широко используемый в электроэнергетике, водоснабжении и нефтегазовой отрасли. Поддерживает метки времени, 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

Почему Go?

Три причины:

  1. Горутины. Сканирование сети — задача с высоким параллелизмом. На /24-подсети это 254 хоста × 3 протокола = 762 потенциальных соединения. Go позволяет запускать их конкурентно без головной боли с thread pools.

  2. Статическая компиляция. Бинарник не тянет зависимостей. Скопировал на машину — запустил. Для полевого аудита это критично.

  3. Стандартная библиотека. Встроенный 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).

REST API

Помимо 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 приветствуются. Если занимаетесь ОТ‑безопасностью или просто интересуетесь темой — буду рад обратной связи в комментариях.