| 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 | -sX | FIN+PSH+URG 标志 | 异常标志组合 |
| TCP NULL | -sN | 无标志位 | 零标志 TCP 数据包 |
| UDP Scan | -sU | UDP 发到多个端口 | 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:扫描检测分析脚本
"""分析 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(, {})
sig = alert.get(, )
sig:
src_ip = event.get(, )
dst_ip = event.get(, )
dst_port = event.get(, )
ts = datetime.fromisoformat(
event[].replace(, )
)
scanner = .scan_events[src_ip]
scanner[] = src_ip
scanner[].add(dst_ip)
scanner[].add(dst_port)
scanner[] +=
scanner[][sig] +=
sig:
scanner[].add()
sig:
scanner[].add()
sig:
scanner[].add()
sig:
scanner[].add()
sig:
scanner[].add()
sig:
scanner[].add()
sig:
scanner[].add()
sig:
scanner[].add()
scanner[] ts < scanner[]:
scanner[] = ts
scanner[] ts > scanner[]:
scanner[] = ts
(json.JSONDecodeError, KeyError, ValueError):
():
scanners = (
.scan_events.values(),
key= x: x[],
reverse=
)
()
()
()
()
scanner scanners:
targets = (scanner[])
ports = (scanner[])
duration = (scanner[] - scanner[]).total_seconds() \
scanner[] scanner[]
is_internal = scanner[].startswith((, , ))
severity = is_internal \
targets > ports >
()
()
()
()
()
()
()
()
sig, count (
scanner[].items(), key= x: x[], reverse=
)[:]:
()
()
__name__ == :
detector = ScanDetector()
log_file = sys.argv[] (sys.argv) >
detector.process_eve_json(log_file)
detector.generate_report()
响应剧本
- 分类(Triage)——确定扫描是否来自已授权的扫描器或未知来源
- 信息丰富(Enrich)——在威胁情报 feeds 中查询源 IP
- 评估范围——统计唯一目标和端口数量,评估扫描广度
- 封锁——将激进的外部扫描器添加到防火墙封锁列表
- 调查内部——内部扫描可能表明主机被入侵;隔离并调查
- 关联——检查扫描后是否发生了利用尝试
最佳实践
- 将授权扫描器加白名单——对已知漏洞扫描器 IP 抑制告警
- 重点关注内部扫描——内部扫描比外部扫描严重性更高(表明已被入侵)
- 阈值调整——根据环境调整阈值;/16 网络的扫描噪声更多
- 与其他告警关联——将扫描检测与利用告警结合,获得杀伤链可见性
- 基于时间的分析——非常规时间(凌晨 3 点)的扫描需优先处理
- 限制告警速率——防止扫描洪泛使 SIEM 充斥噪声
参考资料
- Suricata 规则文档
- Nmap IDS 规避技术
- OPNsense Suricata Nmap 检测规则
- Emerging Threats 规则集