简介《网络安全中攻击者画像的关键技术研究.pdf》是一份聚焦网络防御中攻击者画像构建的学术研究资料适合网络安全领域的研究人员、高校师生及安全运维工程师阅读。文档系统梳理了攻击者画像的作用与研究现状从传统IP定位与追踪的局限性切入重点剖析威胁情报、机器学习、网络行为画像三大关键技术并从多源信息融合、实时在线学习、隐私保护等方面展望未来趋势有助于读者建立从攻击行为识别到画像构建的完整知识框架。资源共包含1个PDF文件容量约1008KB内容以论文形式呈现结构清晰、术语规范包含摘要、关键词、正文与参考文献兼具理论深度与实践参考价值。已有290人学习下载对于正在进行网络安全威胁分析、态势感知或相关课题写作的读者尤为适用。1. 在网络安全应急响应里攻击者画像要解决的不是“抓人”在网络安全应急响应里处置完一台被挖矿的机器容易回答“这到底是谁干的”却很难。攻击者画像要解决的就是这种问题把扫描、投递、执行、回连、横移这类原始告警聚合成一个能解释、能更新、能复用的行为人档案。它的价值不是精确定位某个人而是让你在下次攻击发生时更快认出来。这个方向适合两类人一类是刚走完网络安全学习路线、正往威胁分析方向走的新人另一类是手头日志不少但不知道怎么建模的网络安全工程师。前者需要一条从数据到结论的完整路径后者需要知道哪些环节最容易被坑。下文从数据组织、行为结构、算法建模到验证逐层展开所有方法都站在防御方视角偏重可落地。2. 画像的数据底座把日志、样本与威胁情报揉成一张事实表做攻击者画像第一步不是选算法而是想清楚“画像吃什么数据”。很多项目上来就堆告警最后画像库变成告警流水账根源在于没有先把数据组织成“事实”。2.1 画像数据从哪里来终端日志、流量元数据与威胁情报的取舍一次完整的攻击在终端和网络两侧都会留下痕迹。画像要覆盖四个数据来源缺一个就会“偏科”。数据来源典型字段画像里的作用常见数据坑终端日志进程路径、命令行、文件哈希、登录源IP确认工具与手法还原投递链字段缺失、命令行被截断流量元数据通信对端IP:端口、DNS请求、TLS指纹找基础设施和回连节奏加密流量多、留存周期短威胁情报信誉标签、IOC、团伙描述关联已知工具族与攻击组织时效滞后、来源间口径不一历史事件事件工单、处置记录、样本标签提供“正确答案”用来验证画像记录松散、可量化程度低终端日志决定你能把攻击链还原到多细流量元数据决定你能不能发现终端看不到的回连行为威胁情报负责把当前行为和已知团伙对上号历史事件则是一面镜子。我一般会把威胁情报放在“关联”而不是“判定”的位置——直接用情报标签当画像主体容易被情报噪声带偏。更稳妥的做法是先用自己的终端和流量数据把行为结构搭出来情报只负责给画像附加上下文。2.2 字段对齐与实体归一先用 Python 建好画像事实表多数据源同时接进来第一件事是“打架”。同一台主机一个系统里叫 hostname另一个叫 computer_name威胁情报里又叫 host同一个文件哈希大写小写混着存。不统一口径后面做画像关联会到处断链。常见做法是先做一张事件宽表把关键字段抽出来清洗。下面这段代码是我做画像底座时的最小骨架import pandas as pd def build_fact_table(alerts: pd.DataFrame) - pd.DataFrame: # 统一时间戳原始数据可能是字符串、秒级 epoch统一成 UTC 秒 alerts[event_ts] pd.to_datetime(alerts[event_ts], units) # 源 IP 去空格并转字符串空值统一成 UNKNOWN避免统计时被 NaN 拆散 alerts[src_ip] alerts[src_ip].astype(str).str.strip().fillna(UNKNOWN) # 样本哈希统一为小写不同设备输出大小写不一致不统一会断关联 alerts[file_hash] alerts[file_hash].str.lower().fillna() # 目标端口转成可空整数用 Int64 而不是 float避免空端口变成 0.0 alerts[dst_port] alerts[dst_port].astype(Int64) # 拼接一个事件 ID用于后面按实体聚合时不重复计数 alerts[event_id] ( alerts[event_ts].astype(int64).astype(str) | alerts[src_ip] | alerts[rule_id].astype(str) ) return alerts[[event_id, event_ts, src_ip, dst_port, file_hash]]逻辑说明事件 ID 是画像聚合的锚点同一时间、同一源 IP、同一检测规则命中的多条告警会被压缩成一个事件避免同一行为被重复计算。时间戳统一成 UTC 秒是为了让来自 EDR、防火墙、DNS 服务器的记录能按时间对齐后面做时间线切分才不混乱。参数注意units是按秒级时间戳写的如果你的原始数据是毫秒改为unitmsInt64是 pandas 的可空整数类型端口为空时不会参与统计但也不会被当成 0fillna()只适合对字符串列做空值填充数值列不要照抄。2.3 画像对象选谁IP、域名、样本哈希到底给谁建档数据洗好之后真正影响成败的判断是“给谁建画像”。主体选不对后面算法再准也白搭。我的经验是文件哈希最适合当稳定主体一个工具被反复使用它的哈希和编译特征会形成稳定的“工具指纹”域名适合当作基础设施画像因为它比 IP 活得久IP 生命周期太短尤其是云主机和跳板节点单独用 IP 建画像会导致画像频繁失效。但 IP 又不能完全丢掉它是连接主机行为和外网基础设施之间的纽带。实际操作中我会建一张实体主表每个实体有类型前缀比如hash:abc123、domain:evil.example、ip:1.2.3.4同时记录首现时间、末现时间、活跃天数和证据数。画像主体选文件哈希和域名IP 作为关联属性附着在画像下面。这样既能保证画像稳定又能在告警时通过 IP 快速拉出整条关系链。3. 用攻防视角把画像结构化战术、技术与时间线画像不能只是一堆数值特征运营人员需要的是能看懂的行为描述。这里要用攻防框架把原始事件转成结构化标签。3.1 把攻击行为映射到战术标签ATTCK 映射工作流ATTCK 不是标准但它是目前最通用的攻击行为描述框架。画像结构化时我会把原始告警映射到战术和技术两级做法分四步导出检测规则命中的原始事件保留观测值比如命令行、目标端口、文件路径。对每个事件抽“动作对象”命令执行、文件落地、网络回连、权限变更。按 ATTCK 战术编号打标签战术层比如初始访问、执行、持久化、C2。人工抽样复核确认映射没有把“扫描”误标成“入侵”。映射结果就像一张翻译表。举个例子原始观测对应的攻击行为ATTCK 技术置信度powershell 远程下载执行无文件执行T1059.001中高多次 DNS A 查询同一域名潜在回连T1071.001中注册表 Run 键写入持久化T1547.001高不推荐把所有告警都映射到子技术层一般到技术这一级对画像已经够用。字段缺失严重的告警宁可不映射也不要硬猜一个标签塞进去。我把“未映射比例”本身当作画像的一个质量指标未映射占比超过 40% 的画像置信度自动降一级。3.2 攻击时间线切分从初始访问到痕迹清理的最小状态机画像不光要知道攻击者做了什么还要知道他在什么时候做的。同一行为出现在侦察阶段和出现在内网横移阶段意义完全不同。我用一个简单的状态机按时间顺序切分攻击阶段。核心逻辑是事件先按时间排序再把每个原始事件匹配到阶段关键字阶段变化时才写入时间线相同阶段重复出现不重复计数。PHASE_KEYWORDS { initial_access: [bruteforce, exploit, phishing], execution: [powershell, cmd, script, rundll32], persistence: [registry, service, schedule], c2: [beacon, dns_query, http_post, ssl], impact: [ransom, miner, exfil, delete] } def assign_phases(events): # events: [{ts: 1700000000, event_type: powershell_download, event_id: abc}] events_sorted sorted(events, keylambda x: x[ts]) current_phase None timeline [] for ev in events_sorted: matched None for phase, keywords in PHASE_KEYWORDS.items(): if any(kw in ev[event_type].lower() for kw in keywords): matched phase break if matched and matched ! current_phase: timeline.append({ ts: ev[ts], phase: matched, evidence: ev[event_id] }) current_phase matched return timeline逻辑说明先按时间排序保证阶段顺序正确current_phase避免同阶段高频告警把时间线撑爆每个阶段只保留第一个事件作为证据足够支撑“什么时候进入这个阶段”的判断。输出结果是一份攻击阶段序列比如initial_access - execution - c2这张序列本身就是画像里最有区分度的特征之一。参数注意PHASE_KEYWORDS里的关键词要和你们自己的告警类型命名对齐比如你们平台把 WebShell 告警叫webshell_upload就往initial_access里加webshell。用in做子串匹配容易误命中比如cmd会撞上cmdline建议改成正则表匹配匹配不到的事件不要硬分阶段。3.3 画像存储选型ES 标签检索与图库关系查询怎么搭结构化完成后要考虑画像存哪里。不选型的常见结果CSV 存全量后期查询拉胯全塞关系库画像之间的关系查询写得想骂人。我一般分两层存。画像主体属性、战术标签、时间线用全文检索引擎按entity_id phase tag查询主要解决“这个画像有哪些特征”的问题。实体之间的关系用图数据库存主要解决“这个 IP 连接过哪些样本、这些样本又关联到哪个域名”的问题。如果团队没有图库用一张同源关系表也能撑住前期字段示例说明entity_ahash:abc123主体ID带类型前缀relationsame_domain关系类型entity_bdomain:evil.example关联对象weight0.8关联强度由共现次数决定last_seen2025-01-12最近一次观察到的时间关系表的优势是军用先建库直接用 SQL 就能查“和这个哈希共享过同一域名的其他实体”。当关系数量超过百万级再迁移到图数据库。提前上图库反而会增加维护成本。4. 画像建模的三种关键技术聚类、图分析与周期识别数据结构和时间线都齐了接下来才进入“建模”环节。这一章选三种最常用也最容易出效果的技术展开聚类负责分群图分析负责找核心周期识别负责看节奏。4.1 特征工程与聚类把一群告警聚成一个“团伙”聚类解决的是“这些攻击是不是同一拨人干的”。特征选择上我不用 IP 当特征因为 IP 会漂移用行为侧特征更稳比如源 IP 数量、目标端口数、样本哈希数、回连间隔方差。聚类前必须做标准化否则端口数这种绝对值大的特征会压过方差类特征。from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score X df[[dst_port_count, file_hash_count, beacon_interval_var, payload_entropy]] # 标准化消除量纲差异否则 dst_port_count 会主导距离计算 scaler StandardScaler() X_std scaler.fit_transform(X) # 用轮廓系数挑 k而不是直接看惯性 best_k 2 best_score -1 for k in range(2, 10): km KMeans(n_clustersk, random_state42).fit(X_std) score silhouette_score(X_std, km.labels_) if score best_score: best_score score best_k k km KMeans(n_clustersbest_k, random_state42) df[group_id] km.fit_predict(X_std)逻辑说明轮廓系数反映的是“簇内紧密、簇间分离”的程度比惯性更直观。random_state42固定随机种子保证每次运行结果一致不然安全团队复查时没法复现你当初的分组结果。参数注意特征列越少越好缺失率超过 70% 的列不要入模空值不要直接填 0否则会制造出“全是 0 的一簇”。轮廓系数低于 0.25 时说明数据本身分不开这时候不要硬聚类退回规则分组更实际。4.2 基础设施关联用图算法找出隐藏的指挥节点聚类能分团伙但不知道团伙里的哪个节点是核心。这时候把实体关系建模成图受害主机连过的外部域名、IP、证书、样本哈希都是节点连线表示“出现过关系”。如果几十台受害主机都连过同一个外部节点这个节点的可疑程度会迅速上升。import networkx as nx # edges 格式: [(src_entity, dst_entity), ...] G nx.Graph() G.add_edges_from(edges) # 介数中心性衡量节点在多少对节点之间的最短路径上 cent nx.betweenness_centrality(G) top_nodes sorted(cent.items(), keylambda x: x[1], reverseTrue)[:10]逻辑说明介数中心性高的节点往往是图里的“桥”连接着多个小团伙。在画像场景里这类节点通常对应共享的基础设施比如同一个分发域名、同一个回连服务器。top_nodes可以直接作为“疑似指挥节点”列表输出。参数注意如果整个图是一个大连通分量中心性会失真所有路径都经过少数几个节点。先做连通分量切分只在大分量内部算中心性边权重可以用共现次数归一化到 0-1再走加权中心性效果比纯拓扑更好。4.3 行为节奏画像识别攻击任务的周期与频率指纹攻击者的自动化任务往往有固定节奏比如每 5 分钟回连一次、每 6 小时扫描一轮。把时间戳换成差分序列做频率分析能自动发现这种周期。import numpy as np # timestamps: 按升序排列的攻击事件时间戳秒级 diffs np.diff(sorted(timestamps)) if len(diffs) 10: # 对差分序列做 FFT周期信号会在某个频率位置出现尖峰 spectrum np.abs(np.fft.rfft(diffs - diffs.mean())) freqs np.fft.rfftfreq(len(diffs)) top_freqs freqs[np.argsort(spectrum)[::-1][:3]] periods 1.0 / top_freqs # 把频率转成周期逻辑说明先对时间戳排序再做一阶差分得到的序列代表“两次攻击之间的间隔”。如果攻击是准周期的这个间隔序列近似恒定FFT 后会出现明显尖峰。periods就是画像里的“节奏指纹”比如输出 300.5 秒说明攻击者大约每 5 分钟活动一次。参数注意样本量太少时不要做频谱分析时间戳少于 10 条基本没有统计意义。周期抖动超过 30% 时不要当成固定周期只能标记为“准周期”。这个指标更适合用来做关联特征而不是单独下结论——一个 300 秒的回连周期结合它连的域名信誉比单独拿出来说“这是攻击行为”要可靠得多。三种方法做一个简单对比方便选型方法适合解决的问题数据要求最容易踩的坑聚类攻击者分群特征完整、无大量缺失特征没归一化变量互相打架图中心性发现核心节点关系数据完整全图连通时中心性失真周期识别判断攻击节奏事件时间戳精确数量够非平稳数据容易出伪周期5. 攻击者画像落地避坑五个让画像失真的具体问题画像建模跑通之后真正考验人的是落地过程中的细节。下面五个问题都是我实际遇到过的按“现象、原因、解决”写清楚。5.1 把“跳板”当成了“攻击者本人”现象一个外部主机 IP 连续两周对内部网络发起扫描画像直接把它标记为“核心攻击者”高置信度告警触发封禁。后来发现这是公网上一台被控跳板机真正的操作者经常换节点。原因画像主体选错了维度把临时 IP 当成了稳定实体。攻击者不会傻到长时间用一个 IP但中间跳板会。解决IP 只作为关联属性画像主体用文件哈希和域名。如果非要给 IP 建档必须加入驻留时长和行为一致性校验驻留时间短、行为变化大的 IP只能给弱标签不允许直接产出最终画像。5.2 时间窗口一刀切团伙被“切”成两拨人现象同一团伙在 1 月活跃一周、2 月又活跃一周中间隔了 20 天。用固定“过去 7 天”做窗口聚类两次活动被拆成两个画像后续关联始终合不到一起。原因固定时间窗口没有考虑攻击者的休眠期。很多攻击活动是间歇性的不是持续不断的。解决用事件间隔动态决定窗口把“最大间隔小于 3 天”的连续活动串成一个作案片段再让画像中有多个片段。之后做滑动窗口更新而不是每次全量重算。5.3 聚类结果一团糊所有样本都在一个簇里现象KMeans 跑完轮廓系数只有 0.12散点图上看全是一坨运营根本没法用。原因特征列堆了太多空值统一填 0距离计算被“全 0”主导还有的特征没做标准化绝对值大的列把其他列全部淹没。解决建模前先看每列缺失率超过 70% 的列直接扔掉缺失列单独标记不让空值参与距离计算聚类后必须先看轮廓系数低于 0.25 就退回规则分组不要硬上聚类。5.4 画像库变成“僵尸库”半年后还在告警现象半年前建好的画像现在还在每天触发告警运营已经被刷屏刷到不看告警了。原因画像没有生命周期管理。攻击者的基础设施会废弃、域名会过期、工具会换画像却不死。解决给画像加“最近证据时间”字段。超过 N 天没有新证据自动降级为休眠状态恢复活跃且证据增加再重新升级。N 一般取该画像所属攻击阶段的中位时间间隔乘以 3避免周期长的活动被误杀。5.5 画像很准但响应不了现象画像判定“这就是那拨人”但分析员不敢处置因为拿不出可以给领导看的完整证据。原因画像只输出“是谁”不输出“凭什么”证据链断层。解决画像输出结构体里强制带证据列表攻击时间线、原始日志 ID、样本哈希、映射到的 ATTCK 技术编号。将这个结构体直接对接工单系统的描述字段让处置人员打开工单就能看到完整证据链。提示以上五条没有一条是算法问题全是数据治理和工程决策问题。画像项目做久了就会明白建模只占三成功夫七成都花在数据掉链子上。6. 验证画像的成色用回测和影子模式代替直觉画像建完最重要的事是验证。不能因为几个案例对上了就说“这套方案有效”。我用的是两板斧回测和影子模式。回测的做法是从历史工单系统里把已确认的攻击事件翻出来作为正样本集再把画像规则跑一遍按置信度排序看真正的攻击者排在前几名。计算指标用precisionk比整体准确率更贴近实战——安全运营的场景里你只需要关心排序头部有没有命中。def precision_at_k(candidates, confirmed, k10): # candidates: 按置信度降序排列的候选画像 ID 列表 # confirmed: 已确认属于真实攻击者的画像 ID 集合 hit len(set(candidates[:k]) set(confirmed)) return hit / k这个函数简单但能回答最关键的问题前 10 个高置信画像里有几个是真的。我一般要求这个值不低于 0.7低于这个数说明画像规则里的噪声太多需要回去查特征工程。进阶一点的做法是画像版本对比。把新规则和老规则同时跑在同一份历史数据上对比precisionk和误报数量。不要急着全量上线先开“影子模式”新画像只写日志不触发告警观察一到两周看误报率和运营接受度再决定开放。如果团队有 SRC 漏洞众测平台可以把平台上已确认的漏洞报告也汇入回测集让正样本数量更扎实。这块是我做画像项目最大的教训模型好坏不是看训练时的 loss而是看回测集上的precisionk。我习惯每季度从工单系统导出一批已确认事件跑一遍当季的画像规则这一轮比任何调参都值。希望帮到你下次被问“这画像准不准”时你也能直接甩出一份回测数据而不是凭感觉。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?