L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

detecting-network-scanning-with-ids-signatures

detecting-network-scanning-with-ids-signatures

使用 Suricata 和 Snort IDS 签名、基于阈值的检测规则和流量异常分析,检测网络侦察和端口扫描,识别 Nmap、Masscan 及自定义扫描活动。

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: detecting-network-scanning-with-ids-signatures description: 使用 Suricata 和 Snort IDS 签名、基于阈值的检测规则和流量异常分析,检测网络侦察和端口扫描,识别 Nmap、Masscan 及自定义扫描活动。 domain: cybersecurity subdomain: network-security tags: [ids, nmap-detection, port-scanning, snort, suricata, reconnaissance, network-security, signature-detection, threshold-rules] version: "1.0" author: mahipal license: Apache-2.0

使用 IDS 签名检测网络扫描

概述

网络扫描(network scanning)通常是攻击的第一阶段,攻击者使用 Nmap、Masscan、ZMap 和自定义扫描工具枚举活跃主机、开放端口、运行中的服务和操作系统版本。检测这种侦察活动可以为潜在攻击提供早期预警。Suricata 和 Snort 等 IDS/IPS 系统可通过三种方式识别扫描:基于签名的检测(匹配已知扫描工具的数据包模式)、基于阈值的检测(统计一段时间内的连接尝试次数)和异常检测(识别异常流量模式)。本技能涵盖编写和部署用于扫描检测的 IDS 签名、配置基于阈值的告警,以及将扫描活动与下游攻击指标进行关联。

前置条件

  • 以 IDS/IPS 模式部署 Suricata 7.0+ 或 Snort 3.0+
  • 用于流量可见性的网络 TAP 或 SPAN 端口
  • 启用 Emerging Threats 规则集
  • 用于告警分析的日志基础设施(ELK Stack、Splunk)
  • 了解正常网络流量模式的基线知识

核心概念

扫描技术与检测指标

扫描类型Nmap 参数数据包特征检测方法
TCP SYN-sS仅 SYN 标志,不完成握手无 SYN/ACK 响应的 SYN 模式
TCP Connect-sT完整三次握手来自单一来源的多个连接
TCP FIN-sF仅 FIN 标志FIN 发到关闭端口(RST 响应)
TCP Xmas-sXFIN+PSH+URG 标志异常标志组合
TCP NULL-sN无标志位零标志 TCP 数据包
UDP Scan-sUUDP 发到多个端口ICMP 端口不可达响应
ACK Scan-sA仅 ACK 标志(探测防火墙)未经请求的 ACK 数据包
SYN/ACK Scan自定义没有先前 SYN 的 SYN+ACK状态违规
OS 指纹识别-O异常 TCP 选项/窗口大小特定选项组合
版本检测-sV服务探测字符串已知探测载荷

Nmap 时序模板

模板Nmap 参数速度检测难度
Paranoid-T0每 5 分钟 1 个探测极难
Sneaky-T1每 15 秒 1 个探测困难
Polite-T2每 0.4 秒 1 个探测中等
Normal-T3默认并行度容易
Aggressive-T4并行,1.25 秒超时很容易
Insane-T5最大并行度极易

实施步骤

步骤 1:部署 Suricata 扫描检测规则

创建 /var/lib/suricata/rules/scan-detection.rules:

# === TCP 扫描检测 ===

# 检测 TCP SYN 扫描(大量 SYN 但未完成)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP SYN Scan Detected"; flags:S,12; threshold:type both,track by_src,count 30,seconds 10; classtype:attempted-recon; sid:5000001; rev:2;)

# 检测 TCP FIN 扫描
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP FIN Scan"; flags:F,12; threshold:type both,track by_src,count 20,seconds 60; classtype:attempted-recon; sid:5000002; rev:1;)

# 检测 TCP Xmas 扫描(FIN+PSH+URG)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP Xmas Tree Scan"; flags:FPU,12; classtype:attempted-recon; sid:5000003; rev:1;)

# 检测 TCP NULL 扫描(无标志)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP NULL Scan"; flags:0,12; classtype:attempted-recon; sid:5000004; rev:1;)

# 检测 ACK 扫描(探测防火墙)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP ACK Scan"; flags:A,12; flow:stateless; threshold:type both,track by_src,count 50,seconds 30; classtype:attempted-recon; sid:5000005; rev:1;)

# 检测 SYN+ACK 扫描(异常无状态探测)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN TCP SYN-ACK Scan"; flags:SA,12; flow:stateless; threshold:type both,track by_src,count 30,seconds 30; classtype:attempted-recon; sid:5000006; rev:1;)

# === UDP 扫描检测 ===

# 检测 UDP 端口扫描
alert udp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN UDP Port Scan"; threshold:type both,track by_src,count 30,seconds 10; classtype:attempted-recon; sid:5000010; rev:1;)

# === Nmap 专项检测 ===

# 检测 Nmap 操作系统指纹识别(T1 探测——ECN SYN)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap OS Fingerprint Probe"; flags:SEC,12; window:1; classtype:attempted-recon; sid:5000020; rev:1;)

# 检测 Nmap 窗口扫描(特定窗口大小模式)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap Window Size Probe"; flags:A,12; flow:stateless; window:1024; threshold:type both,track by_src,count 10,seconds 30; classtype:attempted-recon; sid:5000021; rev:1;)

# 检测 Nmap 版本检测探测
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap Service Version Probe"; flow:established; content:"HELP"; depth:4; threshold:type both,track by_src,count 5,seconds 60; classtype:attempted-recon; sid:5000022; rev:1;)

# 检测 Nmap 脚本引擎(NSE)
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Nmap NSE HTTP Script"; http.user_agent; content:"Nmap Scripting Engine"; classtype:attempted-recon; sid:5000023; rev:1;)

# === Masscan 检测 ===

# 检测 Masscan SYN 扫描(特定窗口大小)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"SCAN Masscan SYN Scan Detected"; flags:S,12; window:1024; threshold:type both,track by_src,count 100,seconds 10; classtype:attempted-recon; sid:5000030; rev:1;)

# === 内部扫描检测 ===

# 检测内部主机扫描(横向移动侦察)
alert tcp $HOME_NET any -> $HOME_NET any (msg:"SCAN Internal Network Scan Detected"; flags:S,12; threshold:type both,track by_src,count 50,seconds 30; classtype:attempted-recon; sid:5000040; rev:1;)

# 检测内部 ICMP 扫描
alert icmp $HOME_NET any -> $HOME_NET any (msg:"SCAN Internal ICMP Sweep"; itype:8; threshold:type both,track by_src,count 30,seconds 10; classtype:attempted-recon; sid:5000041; rev:1;)

步骤 2:配置基于阈值的检测

编辑 /etc/suricata/threshold.config:

# 对已授权的漏洞扫描器抑制扫描告警
suppress gen_id 1, sig_id 5000001, track by_src, ip 10.0.5.100
suppress gen_id 1, sig_id 5000001, track by_src, ip 10.0.5.101

# 限制扫描告警速率,防止日志洪泛
rate_filter gen_id 1, sig_id 5000001, track by_src, count 5, seconds 300, new_action alert, timeout 600
rate_filter gen_id 1, sig_id 5000040, track by_src, count 3, seconds 300, new_action alert, timeout 600

# 关键内部扫描的事件过滤
event_filter gen_id 1, sig_id 5000040, type both, track by_src, count 1, seconds 60

步骤 3:扫描检测分析脚本

#!/usr/bin/env python3
"""分析 IDS 告警中的网络扫描活动并生成报告。"""

import json
import sys
from collections import defaultdict
from datetime import datetime


class ScanDetector:
    """关联 IDS 告警以识别扫描活动。"""

    def __init__(self):
        self.scan_events = defaultdict(lambda: {
            'source_ip': '',
            'target_ips': set(),
            'target_ports': set(),
            'scan_types': set(),
            'alert_count': 0,
            'first_seen': None,
            'last_seen': None,
            'signatures': defaultdict(int),
        })

    def process_eve_json(self, filepath: str):
        """处理 Suricata EVE JSON 告警日志。"""
        with open(filepath, 'r') as f:
            for line in f:
                try:
                    event = json.loads(line)
                    if event.get('event_type') != 'alert':
                        continue

                    alert = event.get('alert', {})
                    sig = alert.get('signature', '')

                    if 'SCAN' not in sig:
                        continue

                    src_ip = event.get('src_ip', '')
                    dst_ip = event.get('dest_ip', '')
                    dst_port = event.get('dest_port', 0)
                    ts = datetime.fromisoformat(
                        event['timestamp'].replace('Z', '+00:00')
                    )

                    scanner = self.scan_events[src_ip]
                    scanner['source_ip'] = src_ip
                    scanner['target_ips'].add(dst_ip)
                    scanner['target_ports'].add(dst_port)
                    scanner['alert_count'] += 1
                    scanner['signatures'][sig] += 1

                    if 'SYN' in sig:
                        scanner['scan_types'].add('SYN 扫描')
                    elif 'FIN' in sig:
                        scanner['scan_types'].add('FIN 扫描')
                    elif 'Xmas' in sig:
                        scanner['scan_types'].add('Xmas 扫描')
                    elif 'NULL' in sig:
                        scanner['scan_types'].add('NULL 扫描')
                    elif 'UDP' in sig:
                        scanner['scan_types'].add('UDP 扫描')
                    elif 'Nmap' in sig:
                        scanner['scan_types'].add('检测到 Nmap')
                    elif 'Masscan' in sig:
                        scanner['scan_types'].add('检测到 Masscan')
                    elif 'Internal' in sig:
                        scanner['scan_types'].add('内部扫描')

                    if scanner['first_seen'] is None or ts < scanner['first_seen']:
                        scanner['first_seen'] = ts
                    if scanner['last_seen'] is None or ts > scanner['last_seen']:
                        scanner['last_seen'] = ts

                except (json.JSONDecodeError, KeyError, ValueError):
                    continue

    def generate_report(self):
        """生成扫描检测报告。"""
        scanners = sorted(
            self.scan_events.values(),
            key=lambda x: x['alert_count'],
            reverse=True
        )

        print(f"\n{'='*70}")
        print("网络扫描检测报告")
        print(f"{'='*70}")
        print(f"唯一扫描来源:{len(scanners)}\n")

        for scanner in scanners:
            targets = len(scanner['target_ips'])
            ports = len(scanner['target_ports'])
            duration = (scanner['last_seen'] - scanner['first_seen']).total_seconds() \
                if scanner['first_seen'] and scanner['last_seen'] else 0

            is_internal = scanner['source_ip'].startswith(('10.', '172.', '192.168.'))
            severity = "严重" if is_internal else \
                       "高" if targets > 50 or ports > 100 else "中"

            print(f"[{severity}] 扫描源:{scanner['source_ip']}")
            print(f"  类型:{'内部' if is_internal else '外部'}")
            print(f"  扫描类型:{', '.join(scanner['scan_types'])}")
            print(f"  目标主机数:{targets},目标端口数:{ports}")
            print(f"  告警总数:{scanner['alert_count']}")
            print(f"  持续时间:{duration:.0f} 秒")
            print(f"  首次发现:{scanner['first_seen']}")
            print(f"  主要签名:")
            for sig, count in sorted(
                scanner['signatures'].items(), key=lambda x: x[1], reverse=True
            )[:5]:
                print(f"    - {sig}:{count}")
            print()


if __name__ == '__main__':
    detector = ScanDetector()
    log_file = sys.argv[1] if len(sys.argv) > 1 else '/var/log/suricata/eve.json'
    detector.process_eve_json(log_file)
    detector.generate_report()

响应剧本

  1. 分类(Triage)——确定扫描是否来自已授权的扫描器或未知来源
  2. 信息丰富(Enrich)——在威胁情报 feeds 中查询源 IP
  3. 评估范围——统计唯一目标和端口数量,评估扫描广度
  4. 封锁——将激进的外部扫描器添加到防火墙封锁列表
  5. 调查内部——内部扫描可能表明主机被入侵;隔离并调查
  6. 关联——检查扫描后是否发生了利用尝试

最佳实践

  • 将授权扫描器加白名单——对已知漏洞扫描器 IP 抑制告警
  • 重点关注内部扫描——内部扫描比外部扫描严重性更高(表明已被入侵)
  • 阈值调整——根据环境调整阈值;/16 网络的扫描噪声更多
  • 与其他告警关联——将扫描检测与利用告警结合,获得杀伤链可见性
  • 基于时间的分析——非常规时间(凌晨 3 点)的扫描需优先处理
  • 限制告警速率——防止扫描洪泛使 SIEM 充斥噪声

参考资料

  • Suricata 规则文档
  • Nmap IDS 规避技术
  • OPNsense Suricata Nmap 检测规则
  • Emerging Threats 规则集
Skills enregistrées