НейроАгент

Обнаружение сканирования портов: анализ сетевого трафика

Улучшенный алгоритм обнаружения сканирования портов в C++. Анализ временных паттернов, TCP флагов и поведенческих признаков для эффективного мониторинга сетевого трафика.

Вопрос

Как обнаружить сканирование портов по логу сетевого трафика?

Я студент, разрабатываю на C++ сетевой сниффер для мониторинга трафика и обнаружения подозрительной активности. Для анализа сканирования портов я реализовал следующий алгоритм:

cpp
#pragma once
#include "IAnalyzer.hpp"
#include "PacketInfo.h"
#include <string>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <vector>

struct ScanResult {
    std::string srcIp;
    std::string dstIp;
    int uniqueDstPorts;
    bool suspicious;
};

class PortScanningAnalyzer : public IAnalyzer
{
public:
    nlohmann::json analyze(const std::vector<PacketInfo>& packets) override;

private:
    std::vector<ScanResult> detectPortScanning(const std::vector<PacketInfo>& packets, int threshold);
};

nlohmann::json PortScanningAnalyzer::analyze(const std::vector<PacketInfo>& packets) {
    nlohmann::json result;
    result["rule"] = "portScan";

    const int threshold = 100;

    std::vector<ScanResult> scanResults = detectPortScanning(packets, threshold);

    nlohmann::json counts = nlohmann::json::object();
    std::set<std::string> suspiciousIps;

    for (const auto& r : scanResults) {
        counts[r.srcIp] = counts.contains(r.srcIp) ? counts[r.srcIp].get<int>() + r.uniqueDstPorts : r.uniqueDstPorts;

        if (r.suspicious) {
            suspiciousIps.insert(r.srcIp);
        }
    }

    result["dst_ports_count"] = counts;
    result["suspicious_ips"] = suspiciousIps;
    result["threshold"] = threshold;

    return result;
}

std::vector<ScanResult> PortScanningAnalyzer::detectPortScanning(const std::vector<PacketInfo>& packets, int threshold) {
    std::unordered_map<std::string, std::unordered_map<std::string, std::set<int>>> portsMap;

    for (const auto& p : packets) {
        if (p.protocol == "TCP" || p.protocol == "UDP") {
            portsMap[p.srcIp][p.dstIp].insert(p.dstPort);
        }
    }

    std::vector<ScanResult> results;

    for (const auto& [srcIp, dstMap] : portsMap) {
        for (const auto& [dstIp, portSet] : dstMap) {
            ScanResult r;
            r.srcIp = srcIp;
            r.dstIp = dstIp;
            r.uniqueDstPorts = static_cast<int>(portSet.size());
            r.suspicious = r.uniqueDstPorts > threshold;
            results.push_back(r);
        }
    }

    return results;
}

Проблема: при тестировании алгоритма не все попытки сканирования портов обнаруживаются. Например, при сканировании с мобильного устройства уникальные порты не превышали 4-10, а при сканировании с другой машины счетчик остановился на ~100 портах вместо реальных 1000 запросов.

Вопросы:

  1. Какие есть замечания по текущему алгоритму обнаружения сканирования портов?
  2. Какие еще признаки указывают на сканирование портов, кроме количества уникальных портов назначения? (например, широковещательные пакеты)

P.S. Я не специалист по кибербезопасности, поэтому прошу объяснять доступно, без излишнего сленга.

НейроАгент

Основная проблема вашего алгоритма заключается в его чрезмерной простоте - он учитывает только количество уникальных портов назначения, что недостаточно для обнаружения различных типов сканирования портов. Давайте разберем это подробнее.

Содержание

Проблемы текущего алгоритма

Ваш текущий алгоритм имеет несколько существенных недостатков:

1. Игнорирование различных типов сканирования

  • Вы обрабатываете все TCP/UDP пакеты одинаково, хотя существуют разные виды сканирования с разной степенью заметности
  • Например, SYN-сканирование отправляет только SYN пакеты и не устанавливает полное соединение, что делает его менее заметным
  • FIN-, XMAS- и NULL-сканирование используют необычные комбинации флагов, которые могут обходить базовые системы обнаружения

2. Отсутствие временного анализа

  • Ваш алгоритм не учитывает, когда происходят запросы - быстро или медленно
  • Медленные сканирования (slow scans) могут длиться неделями, как упоминается в исследованиях
  • При тестировании с мобильного устройства количество портов было небольшим (4-10), но они могли быть запрошены в течение длительного времени

3. Недостаточный учет характеристик трафика

  • Как показывают исследования, сканеры портов вызывают множество небольших потоков данных, в то время как нормальный трафик имеет больший размер и большую изменчивость
  • Ваш алгоритм не анализирует размер пакетов или последовательность запросов

4. Простой пороговый подход

  • Использование единого порога (100 портов) слишком грубое
  • Нормальный трафик к разным хостам может legitimately запрашивать множество портов
  • Отсутствует адаптация к нормальному поведению сети

Дополнительные признаки сканирования портов

Помимо количества уникальных портов, на сканирование указывают следующие признаки:

1. Временные паттерны

  • Ритмичность запросов: Постоянные интервалы между пакетами к разным портам
  • Высокая частота: Много запросов в краткий промежуток времени
  • Ненормальные временные окна: Активность в нерабочее время или необычные временные паттерны

2. Комбинации флагов TCP

  • FIN-сканирование: Пакеты с установленным только FIN флагом
  • XMAS-сканирование: Пакеты с установленными флагами FIN, PSH и URG
  • NULL-сканирование: Пакеты без установленных флагов

3. Поведение соединений

  • Незавершенные соединения: Много SYN-пакетов без ответов или без установления полного соединения
  • Таймауты: Много запросов, заканчивающихся таймаутом
  • Повторные попытки: Много повторных запросов к одним и тем же портам

4. Статистические аномалии

  • Последовательный vs случайный доступ: Сканирование часто идет последовательно (1,2,3…), а не случайно
  • Шаблоны доступа: Неправильное распределение запросов по портам (например, только порты выше 1024)
  • Отклонение от нормы: Поведение, отличающееся от типичного для данного источника

5. Протокольные особенности

  • ICMP-сканирование: Много ping-запросов (ICMP echo requests) для обнаружения хостов
  • ARP-сканирование: В локальных сетях - много ARP-запросов
  • DNS-запросы: Необычные DNS-запросы для обнаружения сервисов

Улучшенный подход к обнаружению

Исследования показывают, что эффективное обнаружение сканирования портов должно использовать несколько методов одновременно:

Методы на основе анализа потоков данных

  • Анализ размера потоков: Порт-сканеры создают много небольших потоков, в то время как нормальный трафик имеет большие и более изменчивые размеры потоков [источник]
  • Последовательное тестирование гипотез: Позволяет обнаруживать сканирование даже при небольшом количестве портов, если паттерны последовательны

Временное окно анализа

  • Скользящие окна: Анализ трафика в окнах определенного размера (например, 60 секунд)
  • Адаптивные пороги: Пороги, зависящие от нормального поведения сети

Комбинированные индикаторы

  • Весовая система: Разные признаки дают разные баллы (количество портов, скорость, шаблоны)
  • Машина состояний: Отслеживание состояния соединений и паттернов поведения

Практические рекомендации по реализации

1. Добавьте анализ TCP флагов

cpp
struct PacketFlags {
    bool syn;
    bool ack;
    bool fin;
    bool rst;
    bool psh;
    bool urg;
};

// В вашей структуре PacketInfo
PacketInfo {
    // ... существующие поля
    PacketFlags flags;
    int packetSize;
    timestamp_t timestamp;
};

2. Реализуйте временной анализ

cpp
struct TimeWindow {
    std::vector<PacketInfo> packets;
    timestamp_t startTime;
    timestamp_t endTime;
    
    double getPacketRate() const {
        double duration = endTime - startTime;
        return packets.size() / duration;
    }
    
    double getAveragePacketSize() const {
        double totalSize = 0;
        for (const auto& p : packets) {
            totalSize += p.packetSize;
        }
        return totalSize / packets.size();
    }
};

3. Добавьте детектор шаблонов портов

cpp
class PortPatternDetector {
public:
    enum class PatternType {
        SEQUENTIAL,
        RANDOM,
        COMMON_PORTS,
        HIGH_RANGE
    };
    
    PatternType detectPattern(const std::set<int>& ports) {
        // Анализ последовательности портов
        // Поиск паттернов в диапазонах
        // Обнаружение популярных портов
    }
};

4. Реализуйте систему балльной оценки

cpp
class PortScanScorer {
public:
    double calculateSuspicionScore(const std::vector<PacketInfo>& packets) {
        double score = 0.0;
        
        // Базовые признаки
        score += countUniquePorts(packets) * 0.3;
        score += getPacketRate(packets) * 0.2;
        score += getFlagAnomalies(packets) * 0.4;
        score += getTimePatternScore(packets) * 0.1;
        
        return score;
    }
    
private:
    // Вспомогательные методы расчета признаков
};

5. Улучшите основной алгоритм

cpp
std::vector<ScanResult> PortScanningAnalyzer::detectPortScanning(
    const std::vector<PacketInfo>& packets, int threshold) {
    
    // Группировка по временным окнам
    std::vector<TimeWindow> timeWindows = groupIntoTimeWindows(packets, 60); // 60 секунд
    
    std::vector<ScanResult> results;
    
    for (const auto& window : timeWindows) {
        for (const auto& srcIp : getUniqueSources(window)) {
            auto srcPackets = filterBySource(window, srcIp);
            
            // Расчет различных признаков
            int uniquePorts = countUniquePorts(srcPackets);
            double packetRate = calculatePacketRate(srcPackets);
            double avgPacketSize = calculateAveragePacketSize(srcPackets);
            PatternType pattern = detectPortPattern(srcPackets);
            
            // Комбинированная оценка
            double suspicionScore = calculateSuspicionScore({
                uniquePorts, packetRate, avgPacketSize, pattern
            });
            
            if (suspicionScore > SUSPICION_THRESHOLD) {
                results.push_back(createScanResult(srcIp, suspicionScore));
            }
        }
    }
    
    return results;
}

Заключение

Ваш текущий алгоритм является хорошей отправной точкой, но для эффективного обнаружения сканирования портов необходимо учитывать несколько факторов:

  1. Множественные индикаторы: Используйте комбинацию количества портов, временных паттернов, характеристик пакетов и поведенческих шаблонов
  2. Адаптивные пороги: Вместо фиксированного порога используйте динамические пороги, основанные на нормальном поведении сети
  3. Временной анализ: Добавьте анализ трафика во временных окнах для обнаружения медленных сканирований
  4. Разнообразие типов сканирования: Учитывайте различные методы сканирования (SYN, FIN, XMAS и т.д.)

Начните с добавления анализа TCP флагов и временных паттернов, затем постепенно усложняйте алгоритм, добавляя дополнительные признаки. Такой подход позволит обнаруживать как быстрые, так и медленные сканирования, а также различные их типы.

Источники

  1. Detection of slow port scans in flow-based network traffic - PMC
  2. PD-CPS: A practical scheme for detecting covert port scans in high-speed networks - ScienceDirect
  3. What is a Port Scan + How to Detect It - Vectra
  4. Detecting and Understanding Online Port Scans - ExtraHop
  5. The Ultimate Port Scanning Guide - SecOps Solution