인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
생산성 및 협업 · 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

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