简介:《实战网络情报:主动防御》是一本面向信息安全从业者、SOC分析师及企业管理者的实战型电子书,聚焦如何将网络情报真正融入企业安全体系,推动安全运营从被动响应转向主动防御。内容围绕F3EAD模型、OODA循环与网络杀伤链的融合展开,讲解威胁情报整合、基线与异常分析、漏洞管理、风险度量,以及IT与安全团队间的高效协作机制,并通过真实场景案例展示复杂环境下的威胁识别、攻击面收敛与事件响应流程优化。资源包为1个PDF文件,大小约71.28MB,内容完整、排版清晰,便于在电脑或平板上系统阅读与查阅。目前已有53人学习关注,适合希望构建端到端智能安全运营能力、提升情报驱动决策水平的读者参考,也可作为团队内部培训与流程梳理的辅助材料。
1. 实战网络情报:主动防御到底在防什么
很多团队第一次听到「实战网络情报:主动防御」,脑子里浮现的是买一套威胁情报订阅、接几个 IP 黑名单,然后等着告警响。真跑起来才发现,告警是响了,但没人知道该先看哪条,也没人说得清这条情报到底能不能用。问题不在情报本身,而在于把「情报」当成了数据源,而不是一条从采集、研判到处置的闭环链路。
这个标题讲的是:用可落地的网络情报手段,把防御从「等攻击发生再响应」推到「提前识别对手的基础设施和意图」。它适合手里已经有日志、流量、资产台账,但苦于告警噪音大、优先级排不出来的安全运维和应急响应人员。下面按「先立住概念、再动手复现、最后讲坑」的顺序拆开,每一步都尽量给到能直接抄的命令和参数。
2. 情报采集与归一化:把散落的日志变成可查询的线索
主动防御的第一步不是分析,而是让数据能进来、能对齐。很多团队卡在这一步:防火墙、DNS、EDR、蜜罐各说各话,时间戳格式都不一样,查一个 IP 要开四个控制台。这一章先把采集和归一化做扎实,后面才谈得上研判。
2.1 采集源怎么选:先覆盖三类高价值数据
不是所有日志都值得进情报管道。我一般按「能反映对手基础设施」的标准筛,优先接三类:
- DNS 查询日志:对手注册域名、解析 C2 地址,都会在 DNS 留下痕迹。内网递归解析器的日志价值最高。
- 网络流数据:NetFlow 或 Zeek 的 conn.log,能看出内网主机和外部 IP 的通信频次、字节数、持续时间。
- 蜜罐与沙箱回连:蜜罐被扫、沙箱里样本回连的地址,是高质量 IOC 来源。
采集方式上,小规模用 Filebeat 或 Fluent Bit 推送到消息队列,大规模直接上 Kafka。下面是一个 Fluent Bit 采集 DNS 日志并打标签的最小配置:
# fluent-bit.conf [INPUT] Name tail Path /var/log/named/query.log Tag dns.query Parser named_query DB /var/lib/fluent-bit/dns.db [PARSER] Name named_query Format regex Regex ^(?<time>\d{2}-\w{3}-\d{4} \d{2}:\d{2}:\d{2}\.\d+)\s+queries:\s+info:\s+client\s+(?<client_ip>\S+)#\d+.*query:\s+(?<query_name>\S+)\s+(?<query_type>\S+) [OUTPUT] Name kafka Match dns.query Brokers kafka-01:9092,kafka-02:9092 Topics raw-dns Format json这段配置做三件事:tail 插件持续读 DNS 查询日志,regex 解析器把时间、客户端 IP、查询域名、查询类型拆成字段,kafka 输出把结构化数据推到 raw-dns 主题。参数上,DB文件记录读取偏移,重启不会重复消费;Brokers写多个地址避免单点;Format json保证下游能按字段解析。如果日志格式和示例不同,先拿一条真实日志去 regex101 这类工具里调Regex,别硬套。
2.2 归一化字段:统一时间、IP、域名三个维度
数据进来后,不同来源的字段名五花八门。归一化的目标很简单:不管来自 DNS 还是 NetFlow,查一个 IP 时都能用同一个字段名过滤。我一般固定这几个字段:
| 字段名 | 含义 | 示例 |
|---|---|---|
| event_time | 事件时间,统一 UTC 毫秒 | 1710000000000 |
| src_ip | 源 IP | 10.2.3.4 |
| dst_ip | 目的 IP | 203.0.113.5 |
| domain | 域名,无则空 | example-c2.net |
| source_type | 数据来源 | dns / netflow / honeypot |
用 Logstash 做归一化的片段:
# logstash.conf filter { if [Tag] == "dns.query" { mutate { add_field => { "source_type" => "dns" } rename => { "client_ip" => "src_ip" } rename => { "query_name" => "domain" } } date { match => [ "time", "dd-MMM-yyyy HH:mm:ss.SSS" ] target => "event_time" timezone => "UTC" } } if [Tag] == "netflow" { mutate { add_field => { "source_type" => "netflow" } rename => { "srcaddr" => "src_ip" } rename => { "dstaddr" => "dst_ip" } } } }date过滤器把字符串时间转成统一时间戳,timezone => "UTC"避免跨时区查询时对不上。rename把不同来源的字段名统一,后面查src_ip就不用管数据从哪来。注意rename会覆盖同名字段,如果原始数据里已经有src_ip,先确认再改。
2.3 存储选型:时序库和搜索引擎各管一段
归一化后的数据往哪放,取决于查询模式。按 IP 查历史通信、做聚合统计,用 ClickHouse 或 Elasticsearch 都行;如果主要是全文检索域名和 IP 的关联,Elasticsearch 更顺手。我一般这样分:
- 原始日志和全文检索:Elasticsearch,保留 30 天热数据。
- 聚合指标和长期趋势:ClickHouse,保留 180 天,按天分区。
ClickHouse 建表语句示例:
CREATE TABLE netflow_agg ( event_date Date, src_ip String, dst_ip String, domain String, conn_count UInt32, bytes_total UInt64 ) ENGINE = SummingMergeTree() PARTITION BY toYYYYMM(event_date) ORDER BY (event_date, src_ip, dst_ip) TTL event_date + INTERVAL 180 DAY;SummingMergeTree适合预聚合,相同排序键的行会自动合并计数和字节数。PARTITION BY toYYYYMM按月分区,查一个月的数据只扫对应分区。TTL到期自动删旧数据,省得手动清理。如果查询里经常按域名过滤,把domain加到ORDER BY里,但要注意排序键越长写入开销越大,按实际查询频率权衡。
3. 情报研判:从 IOC 到 TTP 的筛选逻辑
数据齐了,接下来是判断哪些线索值得跟进。这一步最容易翻车的地方是:把 IOC 当结论用。一个 IP 出现在威胁情报订阅里,不代表它一定在攻击你;一个域名解析失败,也不代表它没被用作 C2。研判要做的是给线索打分、排优先级。
3.1 IOC 匹配:先做白名单,再做黑名单
直接拿订阅的 IOC 去匹配内网流量,误报会多到没法看。我一般先跑白名单:把常见 CDN、云服务、内部服务的 IP 段和域名排除掉,剩下的再和 IOC 比对。白名单维护在单独的表里,定期更新。
# ioc_match.py import ipaddress WHITELIST_CIDR = [ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "203.0.113.0/24", # 示例:某云服务段 ] def is_whitelisted(ip: str) -> bool: addr = ipaddress.ip_address(ip) return any(addr in ipaddress.ip_network(cidr) for cidr in WHITELIST_CIDR) def match_ioc(flow: dict, ioc_set: set) -> bool: if is_whitelisted(flow["dst_ip"]): return False return flow["dst_ip"] in ioc_set or flow.get("domain") in ioc_setis_whitelisted用ipaddress模块做 CIDR 判断,比字符串前缀匹配可靠。match_ioc先过白名单再查 IOC 集合,集合查询是 O(1),适合大批量流数据。注意白名单要定期 review,有些云服务段会变,写死太久会漏掉真实威胁。
3.2 上下文关联:单条 IOC 不够,要看行为链
一个 IP 被标记为恶意,但如果内网只有一台机器和它通信过一次,优先级不高;如果同一台机器在短时间内先查了可疑域名、又和这个 IP 建立了长连接、还传了大量数据,那就值得立刻看。关联逻辑可以写成规则:
-- 在 ClickHouse 里找同一 src_ip 的关联行为 SELECT src_ip, groupArray(domain) AS domains, groupArray(dst_ip) AS dst_ips, sum(conn_count) AS total_conn, sum(bytes_total) AS total_bytes FROM netflow_agg WHERE event_date >= today() - 7 AND (domain IN (SELECT domain FROM ioc_domains) OR dst_ip IN (SELECT ip FROM ioc_ips)) GROUP BY src_ip HAVING total_conn > 10 OR total_bytes > 10485760 ORDER BY total_bytes DESC LIMIT 50;这条查询把同一源 IP 在一周内命中 IOC 的域名和 IP 聚在一起,HAVING过滤掉通信次数太少或流量太小的,避免噪音。groupArray把关联的域名和 IP 收成数组,方便人工看行为链。参数上,total_conn > 10和total_bytes > 10485760(10MB)是经验值,实际按自己环境的基线调。
3.3 优先级打分:把有限的人力放在最可疑的线索上
研判结果要排序,否则分析师还是不知道先看哪个。我一般用简单加权:
| 维度 | 权重 | 说明 |
|---|---|---|
| IOC 命中类型 | 0.4 | 域名命中比 IP 命中更可疑 |
| 通信频次 | 0.2 | 频次越高越可疑 |
| 数据传输量 | 0.2 | 外传量大加分 |
| 资产重要性 | 0.2 | 核心资产加分 |
打分脚本:
def score_alert(alert: dict) -> float: score = 0.0 if alert.get("ioc_type") == "domain": score += 0.4 elif alert.get("ioc_type") == "ip": score += 0.2 score += min(alert.get("conn_count", 0) / 100, 1.0) * 0.2 score += min(alert.get("bytes_total", 0) / (100 * 1024 * 1024), 1.0) * 0.2 if alert.get("asset_critical"): score += 0.2 return round(score, 2)权重不是固定的,按团队实际误报和漏报情况调。min(..., 1.0)做归一化,避免某个维度数值过大压过其他维度。资产重要性从 CMDB 或资产台账里取,没有的话先手工标核心资产。
4. 主动防御落地:从告警到处置的自动化链路
研判出高优先级线索后,如果还靠人工登录设备封 IP,响应速度跟不上。这一章把处置环节自动化,同时留好回滚和审计。
4.1 封禁策略:先观察再阻断,留后悔药
直接自动封禁风险很大,万一误封核心业务 IP,影响面比攻击本身还大。我一般分两级:高置信度线索自动封禁,中低置信度先加观察名单并通知人工确认。
# response.py import requests def block_ip(ip: str, duration_min: int = 60, dry_run: bool = True): payload = { "action": "block", "ip": ip, "duration": duration_min, "reason": "high_confidence_ioc", } if dry_run: print(f"[dry-run] would block {ip} for {duration_min} min") return resp = requests.post("https://firewall-api.internal/block", json=payload, timeout=5) resp.raise_for_status() print(f"blocked {ip}, ticket={resp.json().get('ticket_id')}")dry_run默认 True,先在测试环境跑通再改 False。duration_min设短一点,比如 60 分钟,到期自动解封,给误封留后悔药。ticket_id用于审计追溯,每次封禁都要能查到是谁、什么时候、因为什么触发的。
4.2 通知与工单:让处置有记录、可回溯
自动封禁的同时要通知到人,否则出了问题没人知道。通知内容至少包含:触发规则、源 IP、目的 IP、命中 IOC、封禁时长、工单号。
# 发送通知到内部工单系统 curl -X POST https://ticket.internal/api/create \ -H "Content-Type: application/json" \ -d '{ "title": "自动封禁: 203.0.113.5", "priority": "high", "description": "src=10.2.3.4 dst=203.0.113.5 ioc=example-c2.net duration=60min", "assignee": "soc-team" }'priority按打分结果映射,高分用 high,中分用 normal。assignee指向值班组,避免工单没人认领。描述里把关键字段写全,后面复盘不用再翻日志。
4.3 验证闭环:封禁后确认是否真的生效
封禁动作发出后,要验证是否生效。常见做法是查防火墙会话表,确认对应 IP 的新建连接被拒绝。
# 在防火墙上查会话 show session filter destination 203.0.113.5 # 预期输出:无新建会话,或状态为 deny如果没有生效,先查 API 返回是否成功,再查防火墙策略顺序,确认封禁规则没有被更宽松的规则覆盖。这一步经常被跳过,导致以为封了其实没封。
5. 避坑与排查:主动防御里最容易翻车的五件事
这一章记录我在实际环境里踩过的坑,每条按现象、原因、解决写,方便对照排查。
5.1 告警风暴:IOC 匹配把 CDN 全打上了
现象:接入威胁情报订阅后,告警量暴涨,大部分是内网访问 CDN 的流量。原因:订阅里的 IOC 包含大量共享 IP,CDN 节点也在其中,没做白名单直接匹配。解决:先维护 CDN 和云服务白名单,匹配前过滤;同时把 IOC 按置信度分级,低置信度的只做观察不告警。
5.2 时间对不上:DNS 日志和 NetFlow 差 8 小时
现象:关联查询时,同一台机器的 DNS 查询和网络流对不上,明明先查域名再连接,时间却反了。原因:DNS 日志用本地时间,NetFlow 用 UTC,归一化时没统一。解决:所有采集端强制 UTC,Logstash 的 date 过滤器显式指定 timezone;存储层查询时也统一按 UTC 展示。
5.3 封禁误伤:自动封了内部跳板机
现象:自动封禁触发后,一台内部跳板机无法访问外部,业务中断。原因:跳板机被入侵后作为攻击源,其 IP 在 IOC 匹配中被命中,自动封禁逻辑没区分资产属性。解决:封禁前查资产台账,内部资产只告警不自动封;封禁动作加 dry-run 阶段,人工确认后再执行。
5.4 情报过期:三个月前的 IOC 还在告警
现象:一个域名三个月前是恶意,现在已失效,但订阅没更新,告警一直响。原因:IOC 没有 TTL,入库后不清理。解决:IOC 入库时带过期时间,默认 30 天,到期自动标记为失效;查询时只匹配未过期的 IOC。
5.5 日志断流:Kafka 磁盘满导致采集停摆
现象:某天开始没有新日志进来,查采集端发现 Kafka 磁盘写满。原因:raw-dns 主题没设保留策略,数据无限堆积。解决:给 Kafka 主题设 retention.ms 和 retention.bytes,比如保留 7 天或 500GB;同时加磁盘监控,到 80% 就告警。
6. 进阶技巧:用蜜罐回连数据反哺情报质量
前面讲的都是拿外部情报来匹配内部流量,这一章反过来:用自己蜜罐捕获的回连数据,验证和补充外部情报。蜜罐被扫描或入侵后,样本往往会回连 C2,这些回连地址是高质量 IOC,而且是你自己环境里真实出现的,比订阅数据更贴合。
具体做法:在 DMZ 放低交互蜜罐,记录所有入站连接和样本回连。回连数据先入 Kafka,再走前面同一套归一化管道,最后和外部 IOC 做交集比对。
# honeypot_feedback.py def compare_honeypot_ioc(honeypot_iocs: set, external_iocs: set): only_honeypot = honeypot_iocs - external_iocs only_external = external_iocs - honeypot_iocs both = honeypot_iocs & external_iocs print(f"仅蜜罐捕获: {len(only_honeypot)}") print(f"仅外部订阅: {len(only_external)}") print(f"两者交集: {len(both)}") # 仅蜜罐捕获的地址优先加入内部黑名单 return only_honeypotonly_honeypot里的地址是外部订阅没覆盖但确实在攻击你的,优先加入内部黑名单。both里的地址可以提升置信度,用于调高告警优先级。only_external里的地址如果长期没在你环境里出现,可以考虑降权,减少噪音。
验证这套逻辑是否有效,可以看两个指标:一是蜜罐捕获的新增 IOC 数量,二是这些 IOC 后续是否真的在内网流量里再次出现。如果连续几周新增 IOC 都是零,要么蜜罐位置不对,要么攻击者已经绕开,需要调整蜜罐的暴露面。
我自己的习惯是每周花半小时看蜜罐回连数据和外部订阅的交集变化,交集突然变大往往意味着有一波针对你所在行业的扫描在发生。这个动作比等告警响更早一步。希望帮到你。
本文还有配套的精品资源,点击获取