1. 为什么攻击路径分析本质上是一个图论问题1.1 网络世界的攻击动作天然就是一张有向图有一次应急处置结束后同事看着一长串关联告警问我如果早一点把这些告警画成一张图是不是就能提前看出攻击者下一步想打哪儿我当时没有立刻回答但这件事让我认真把“网络攻击路径分析”当成一个图论问题来做。后来做得多了越来越觉得这就是一个带权有向图上的推理问题——攻击者的每一步动作本质上都是在图里找一条从入口到目标的可行路。我们拆开看一个典型攻击链扫描探测、漏洞利用、权限提升、横向移动、数据回传。每一步都从一个“状态”跳到另一个“状态”比如某个账号从被爆破到成功登录一台业务服务器再从这台服务器跳板访问数据库。节点是资产、账号、服务和权限边是它们之间的关系攻击者就是沿着这些边一步一步摸到核心数据的。把防守方的资产、账号、网络连通关系、漏洞利用条件全部放进同一个图模型里很多原来只能凭经验判断的问题就有了明确的数学定义。举个生活化的例子你在城市里开车导航路口是节点道路是边攻击路径分析相当于在找一条从当前位置到某个商场的最优路线。区别在于攻击者追求的不是“总里程最短”而是“这条路能不能走通”和“走到头能拿到什么”。所以同样的图论工具最短路径算法、连通性分析、割点检测、中心性计算全部都能用在攻击路径分析上只需要把“距离”换成“利用难度”把“终点”换成“核心资产”。1.2 攻击路径分析要回答的问题图模型都能接住安全团队日常最关心的几个问题放进图模型里以后会变得非常清楚某个互联网入口能不能触达核心数据库对应图的可达性分析。攻击者从入口横向移动到目标最少要经过几台主机对应最短路径问题。哪些主机被多条攻击路径共同经过一旦加固可以同时阻断多条路线对应割点或关键节点识别。内网里哪些主机之间互相可达且账号复用严重可能形成一片容易被整体拿下的区域对应连通分量或社区发现。要阻断所有通向核心资产的路径至少需要修复哪几个漏洞对应最小顶点覆盖的近似求解。把这些需求列出来之后建议就很明确了与其维护无数条分散的告警规则不如先把资产和关系建模成图然后用现成的图算法去回答安全分析问题。这也是我后来所有攻击路径分析工作的底层逻辑。2. 从原始日志到攻击图搭建模型的关键步骤2.1 先想清楚节点和边各代表什么很多初学者一上来就急着把日志倒进图数据库结果建出来的图又乱又没用。我建议先花一天时间定义清楚图里到底有哪些类型的节点哪些边是真正有分析意义的。节点至少分四类主机节点IP、主机名、操作系统、账号节点域账号、本地账号、数据库账号、服务与端口节点开放端口、Web服务、数据库实例、漏洞节点漏洞编号、风险等级。边也对应四类网络可达关系防火墙放行、路由可达、漏洞利用关系某漏洞可以在某主机上被利用、登录认证关系某账号可以登录某主机、权限提升关系从普通用户提升到管理员。场景再复杂一点可以加进程节点、容器节点、数据对象节点但对大多数企业来说上面这四类节点加四类关系已经能覆盖 90% 的横向移动场景。我一再强调建模要克制因为节点类型越多数据清洗和关系推断的复杂度就指数上升最后往往卡在数据治理而不是算法上。2.2 数据源和建图口径建图的数据主要来自五个地方资产清单给节点列表网络拓扑和防火墙策略给“网络可达”边漏洞扫描报告给“漏洞利用”边AD域控日志和主机登录日志给“账号登录”边应用系统访问日志给服务间调用关系。把这五类数据拼到一起基本就是一张真实内网的攻击图。这里有个非常关键的口径问题边到底是“实际发生过”还是“条件允许”。比如某台数据库只对应用服务器开放 3306 端口但防火墙策略显示开发网段也能访问那这条边按实际访问记录建还是按策略允许建我推荐两层都建把实际访问记录作为“高置信度边”把策略允许作为“低置信度边”分析时可以按置信度过滤。只认实际日志会漏掉还没发生的横向移动只认策略又会告警满天飞两层模型的容错性要好得多。数据质量永远比算法复杂度重要。一次实际项目中我因为资产清单少录了一个双网卡服务器导致攻击路径分析结果在图上少了一大块后来把网卡信息补全之后攻击者真正走的路线立刻浮现出来。所以建图之前先花时间把资产台账核对清楚这一步省不掉。2.3 边的权重怎么设代价与收益的统一边的权重不能简单写死成“风险高1风险低0”而是要能同时表达攻击者的代价和收益。我常用的方式是给每条边设置三个属性利用难度、所需权限、目标资产价值。利用难度参考漏洞评分和公开利用工具的存在情况所需权限看登录时需要什么凭证目标资产价值看这台主机上有没有高价值数据或被用作跳板的价值。路径的总分我一般用带权聚合而不是只做简单相加。比如一条路径经过三台主机其中某一台主机的漏洞利用难度极高没有公开工具那整条路径的实际可行性就大打折扣。反过来如果每条边都极易利用哪怕路径长也是高度危险的。具体权重数值每家可以不同但思路是统一的让图模型同时反映“这条路有多容易走”和“走到头能拿多少东西”。另外不要忽略现有控制措施的抵扣效果。如果目标主机上装了终端防护软件、漏洞已通过补丁修复那么对应的边权重应该乘以一个衰减系数。否则模型会把已经修复的漏洞继续当作有效攻击路径分析结果又会失真。3. 实操用Python和NetworkX落地一个路径分析原型3.1 数据预处理与攻击图构建我习惯用 Python 和 NetworkX 做原型验证数据量不大时完全够用企业级全量分析可以在熟悉流程后再迁移到图数据库但核心建图思路是一样的。先把资产清单和关系表读进来统一成节点和边的结构。import networkx as nx import pandas as pd G nx.DiGraph() # 资产节点id、类型、资产价值 assets [ {id: web-01, type: host, value: 6}, {id: db-01, type: host, value: 10}, {id: ad-01, type: host, value: 9}, {id: svc-api, type: service, value: 7}, ] for a in assets: G.add_node(a[id], **a) # 关系边src、dst、关系类型、权重、置信度 edges [ (web-01, svc-api, {relation: 开放端口, weight: 0.3, conf: 0.9}), (svc-api, db-01, {relation: 漏洞可利用, weight: 0.8, conf: 0.7}), (web-01, ad-01, {relation: 账号登录, weight: 0.4, conf: 0.6}), ] for src, dst, attr in edges: G.add_edge(src, dst, **attr)预处理阶段我踩过几个坑一是 IP 有内网和 NAT 两套表示必须先统一成同一套标识二是登录日志里主机名大小写不一致需要先规约三是时间窗口没对齐导致本该关联的先后登录关系被拆成两段。这些清洗规则建议在脚本里固化下来后续每次跑分析都会用到。3.2 路径枚举与可达性分析图建好之后第一件该做的事不是急着枚举路径而是先跑可达性分析看看哪些暴露面最终能触达核心资产。NetworkX 里descendants和all_simple_paths两个函数就够用了。# 查看某入口可达哪些节点 for src in [web-01, svc-api]: reachable nx.descendants(G, src) print(src, 可达节点:, reachable) # 枚举从入口到目标的所有简单路径限制最大跳数 paths list(nx.all_simple_paths(G, web-01, db-01, cutoff4)) for p in paths: print( - .join(p))这里有个必须说清楚的性能陷阱all_simple_paths在节点多的时候会指数爆炸。我见过一个 500 节点的内网图不加任何限制枚举路径跑了一个多小时还没出结果。所以实际使用中先跑可达性分析过滤掉大概率走不通的目标再对高价值目标限制cutoff跳数去枚举路径能砍掉绝大多数计算量。超过 4 跳的路径对多数攻击者来说已经很不现实分析价值有限。3.3 危险评分与关键路径排序路径枚举完之后得给每条路径算一个分数才能排出处置优先级。我常用的评分函数分三段边的利用难度、源节点资产价值、目标节点资产价值权重可以调。def calc_edge_score(edge): util G.edges[edge].get(weight, 0.5) owner_val G.nodes[edge[0]].get(value, 1) target_val G.nodes[edge[1]].get(value, 1) return 0.5 * util 0.3 * target_val 0.2 * owner_val def calc_path_score(path): return sum(calc_edge_score((path[i], path[i 1])) for i in range(len(path) - 1)) scored_paths sorted( ((p, calc_path_score(p)) for p in paths), keylambda x: x[1], reverseTrue ) for p, score in scored_paths[:5]: print(score, - .join(p))注意这只是一个参考公式。你在实际项目里完全可以根据业务特点调整比如你们更担心数据泄露就把目标资产价值的权重调高更担心勒索病毒在内网横向传播就把利用难度的权重调高。关键是让评分逻辑可解释、可调整而不是变成一个黑盒。排序完成后把 Top 路径和对应的漏洞编号、账号信息、目标资产整理成表这份表可以直接交给整改团队。3.4 关键节点挖掘割点与介数中心性除了给路径排序还要找“全局关键点”——那些被多条攻击路径共同经过的主机或账号。这在图论里就是割点和介数中心性的概念。# 介数中心性经过某个节点的最短路径比例 centrality nx.betweenness_centrality(G) print(sorted(centrality.items(), keylambda x: x[1], reverseTrue)) # 割点去掉之后会让图不连通的节点 print(list(nx.articulation_points(G.to_undirected())))为什么这个指标重要假设内网有 20 条通向核心数据库的攻击路径其中有 18 条都要经过某一台运维跳板机那这台跳板机就是全局咽喉节点。只要重点防护它哪怕下面有几百个漏洞没补攻击者能打进来的路子也少了一大半。我实际做过一次分析最后修复清单只列了三个节点但把整个内网横向移动面缩小了 60% 以上。这就是关键节点分析带来的直接收益。4. 实战中经常踩的坑与排查技巧4.1 图断裂数据缺失导致路径走不通第一次拿真实数据建图时最容易出现的现象就是路径走着走着就断了——某台主机日志缺失或者账号关系没采到导致分析结果里核心资产显示“不可达”。这个结论是假的只是数据不全。我的处理办法有三条第一用网络拓扑做兜底凡是从防火墙策略上看得到可达关系的主机对即使没有实际登录日志也补一条低置信度边第二把时间窗口放宽一点再做登录关系聚合很多横向移动前后会隔上几十分钟甚至几小时窗口太窄会拆断路径第三给未知账号单独建一个“未知账号”节点凡是日志里出现但资产库匹配不到的账号都挂上去当作潜在风险入口单独标记出来。补充说明一下置信度字段的作用就在这宁可有低置信度边导致多出一些可解释的告警也不要是因为数据缺失产生一条看起来完全不可达的错误结论。前者还能靠人工复核后者直接就让分析白做了。4.2 图规模爆炸节点多了路径数吃不消模型建得越完整图规模越大。我见过最夸张的一次把 EDR 事件里所有进程调用关系都当成边灌进去全图几十万条边普通的单机脚本直接跑不动。解决思路是分层处理先只保留与高价值资产有可达关系的子图再做跳数限制最后加置信度过滤。具体操作上先筛选出资产价值排名前 20% 的目标节点以这些节点为终点反推可疑的入口集合把入口集合收敛到几十个以内再跑完整的路径枚举。这样计算量能下降一到两个数量级分析结果的可读性反而更好。另外同类节点可以做聚合。比如某网段 100 台 Web 服务器的漏洞情况完全一致访问关系也一致那就没必要建 100 个重复节点合成一个“Web服务器集群”节点即可。聚合之后图小了分析结论也更抽象、更贴近管理层视角。4.3 误报、漏报与阈值调优评分阈值设高了真实风险路径被过滤掉安全团队觉得这个工具没用阈值设低了输出几百条路径每一条都像高危等于没有输出。我调阈值时的做法是把全部路径的分数分布跑出来看直方图按 90 分位、95 分位、99 分位划三条线先以 95 分位作为默认告警线再看实际告警里有多少条最终被确认是有效路径。确认率太低就提高阈值确认率太高说明漏的可能多适当降低。这条思路本质上是在“召回率”和“精确率”之间反复校准。还有一个小技巧给每条边加一个“置信度”字段用置信度做加权后再算路径分这样同样分数的路径里数据来源更充分的那一条会被排在前面。误报的根源往往不是算法而是原始日志本身就被污染了比如扫描流量被当成真实登录记录清洗阶段多下点功夫比调参更有效。5. 从路径分析到真实处置一些值得固化的经验5.1 结果输出不要只给图还要给动作路径分析做得再漂亮如果最后交付的只是一张蜘蛛网一样的图管理者是没法直接用的。我习惯把输出整理成四块内容受影响核心资产清单哪些高价值资产目前存在可达攻击路径风险等级是什么。最危险的三条路径每一条列出入口、中间跳板、目标资产、依赖的关键漏洞或账号。共同脆弱点列表被最多路径经过的主机和账号按介数中心性排序。建议处置清单优先加固哪台机器、禁用哪些账号、修补哪几个漏洞以及每项操作预期能阻断多少条路径。报告里每个结论都必须能回答两个问题先修哪个修了能断掉几条路。路径分析的价值终归体现在决策上而不是那张复杂到没人看得懂的图上。5.2 和现有安全工具联动攻击路径分析不是要替代漏洞扫描、EDR、WAF 这些工具而是在它们之上做一层关联推理。我现在的工作流是这样的漏洞扫描结果导入图模型作为“漏洞利用”边EDR 告警导入作为“账号登录”和“进程调用”边防火墙策略导入作为“网络可达”边然后由路径分析引擎统一算分。这相当于给每个告警增加了一个“上下文维度”单看一条登录告警可能不觉得危险但如果它能连通到核心数据库风险就立刻不一样了。联动之后安全团队的研判效率提升非常明显。原来要人工从几十条告警里还原攻击路径现在图模型直接把可疑路径标出来分析师只需要聚焦确认即可。我参与过的几次攻防演练里红队刚拿到第一台主机权限蓝队就能从图上预测出他们下一步大概会向哪个方向移动提前封堵这就是路径分析跟单点检测最大的区别。5.3 后续可以这样扩展如果基础版的攻击路径分析已经跑通后续有几个扩展方向值得尝试。一个是引入时间维度把静态图升级为动态时序图观察某段时间内新增的可达关系和变化趋势能及时发现新的横向移动苗头。另一个是对入口节点做聚类分析把攻击者常用的人口节点、跳板节点归成一类针对性做暴露面收敛。还有一个是把评分规则沉淀成知识库每次攻防演练后回归验证看哪些路径被真正阻断了哪些因为新增业务又长了出来。别一上来就追求大而全。我建议先把一条真实的高频攻击路径完整建模、分析、处置闭环跑通形成标准和模板再逐步扩大覆盖面。图论给的安全分析带来的是一套可复用、可推理、可验证的方法论而不是一次性输出一张静态图。做了这么多路径分析之后我最大的感受是这个工作的价值不在于算出一条完美的攻击路线来炫技而在于让组织的防御动作有了明确的优先级。与其把几百个漏洞挨个修补不如先把那几台被所有路径共同依赖的关键节点盯死。把图建得越贴近真实环境分析结果离真正可落地的处置方案就越近。如果你正准备在自己的网络里试一次攻击路径分析建议从最小范围开始挑一台核心数据库反推所有可达入口你会发现原来很多你觉得复杂的问题在图上一眼就能看清。