使用机器学习模型检测DLL劫持
DLL劫持是一种常见攻击技术,攻击者用恶意库替换合法进程调用的库。这种技术既被大规模恶意软件(如窃密程序和银行木马)使用,也被APT组织和网络犯罪集团在定向攻击中采用。近年来,DLL劫持攻击数量显著增长。
我们观察到这种技术及其变体(如DLL侧加载)在针对俄罗斯、非洲、韩国等国家和地区组织的定向攻击中使用。Lumma(2025年最活跃的窃密程序之一)使用这种方法进行传播。试图从流行应用程序(如DeepSeek)中获利的威胁行为者也采用DLL劫持。
检测DLL替换攻击并不容易,因为库在合法进程的受信任地址空间内执行。因此,对于安全解决方案来说,此活动可能看起来像是受信任的进程。对受信任进程过度关注可能会影响整体系统性能,因此必须在足够的安全级别和足够的便利性之间取得微妙平衡。
准备
为了确定是否能够训练模型区分恶意和合法的库加载,我们首先需要定义一组高度指示DLL劫持的特征。我们确定了以下关键特征:
- 错误的库位置:许多标准库位于标准目录中,而恶意DLL通常出现在异常位置,如调用它的可执行文件所在文件夹
- 错误的可执行文件位置:攻击者通常将可执行文件保存在非标准路径中,如临时目录或用户文件夹,而不是%Program Files%
- 重命名的可执行文件:为避免检测,攻击者经常将合法应用程序保存为任意名称
- 库大小已更改且不再签名
- 修改的库结构
训练样本和标注
对于训练样本,我们使用了内部自动处理系统提供的动态库加载数据,这些系统每天处理数百万个文件,以及匿名遥测数据,如卡巴斯基用户通过卡巴斯基安全网络自愿提供的数据。
训练样本分三次迭代进行标注。最初,我们无法自动从分析师那里提取表明事件是否为DLL劫持攻击的事件标注。因此,我们使用了仅包含文件信誉的数据库中的数据,并手动标注了其余数据。我们将进程明确合法但DLL明确恶意的库调用事件标注为DLL劫持。然而,这种标注不够充分,因为某些进程(如"svchost")主要设计用于加载各种库。结果,我们基于这些数据训练的模型误报率很高,不适用于实际场景。
在下一轮迭代中,我们按家族额外过滤了恶意库,仅保留那些已知表现出DLL劫持行为的库。基于这些精炼数据训练的模型显示出显著更好的准确性,基本上证实了我们的假设:可以使用机器学习检测此类攻击。
在此阶段,我们的训练数据集有数千万个对象。这包括约2000万个干净文件和约5万个明确恶意的文件。
| 状态 | 总计 | 唯一文件 |
|---|---|---|
| 未知 | ~ 1800万 | ~ 600万 |
| 恶意 | ~ 5万 | ~ 1000 |
| 干净 | ~ 2000万 | ~ 25万 |
我们随后在前代模型的结果上训练后续模型,这些结果已经过分析师验证和进一步标注。此过程显著提高了训练效率。
加载DLL:正常情况是怎样的?
我们有了一个带有大量来自各种进程的库加载事件的标注样本。如何描述"干净"的库?使用进程名称+库名称组合无法考虑重命名的进程。此外,不仅是攻击者,合法用户也可以重命名进程。如果我们使用进程哈希而不是名称,可以解决重命名问题,但这样每个版本的相同库将被视为单独的库。我们最终确定使用库名称+进程签名组合。虽然这种方法将来自单个供应商的所有同名库视为一个,但通常会产生更现实的图像。
为了描述安全的库加载事件,我们使用了一组计数器,包括有关进程的信息(具有给定哈希的文件特定进程名称的频率、具有该哈希的文件特定路径的频率等)、有关库的信息(该库特定路径的频率、合法启动的百分比等)和事件属性(即库是否与调用它的文件在同一目录中)。
结果是一个具有多个聚合(计数器和键的集合)的系统,可以描述输入事件。这些聚合可以包含单个键(例如,DLL的哈希和)或多个键(例如,进程的哈希和+进程签名)。基于这些聚合,我们可以导出一组描述库加载事件的特征。
加载DLL:如何描述劫持
某些特征组合(依赖关系)强烈指示DLL劫持。这些可以是简单的依赖关系。对于某些进程,它们调用的干净库始终位于单独的文件夹中,而恶意库通常放置在进程文件夹中。
其他依赖关系可能更复杂,需要满足多个条件。例如,进程重命名本身并不表示DLL劫持。但是,如果新名称首次出现在数据流中,并且库位于非标准路径上,则很可能是恶意的。
模型演进
在此项目中,我们训练了几代模型。第一代的主要目标是证明机器学习至少可以应用于检测DLL劫持。在训练此模型时,我们使用了该术语最广泛的解释。
模型的工作流程尽可能简单:
- 我们获取数据流并为选定的键集提取频率描述
- 我们从不同时间段获取相同数据流并获得一组特征
- 我们使用类型1标注,其中合法进程从指定的家族集加载恶意库的事件被标记为DLL劫持
- 我们在结果数据上训练模型
第二代模型基于第一代模型处理并经分析师验证的数据(类型2标注)进行训练。因此,标注比第一模型训练时更精确。此外,我们添加了更多特征来描述库结构,并稍微复杂化了描述库加载的工作流程。
基于第二代模型的结果,我们能够识别几种常见的误报类型。例如,训练样本中包含可能不需要的应用程序。这些在某些上下文中可能表现出类似于DLL劫持的行为,但它们不是恶意的,很少属于此类攻击类型。
我们在第三代模型中修复了这些错误。首先,在分析师的帮助下,我们在训练样本中标记了可能不需要的应用程序,以便模型不会检测到它们。其次,在这个新版本中,我们使用了扩展的标注,包括第一代和第二代的有用检测。此外,我们通过独热编码(一种将分类特征转换为二进制格式的技术)扩展了某些字段的特征描述。另外,由于模型处理的事件量随时间增加,此版本添加了基于数据流大小的所有特征归一化。
模型比较
为了评估模型的演进,我们将它们应用于一个它们之前从未处理过的测试数据集。下图显示了每个模型真阳性与假阳性判决的比例。
随着模型的演进,真阳性的百分比增加。虽然第一代模型仅在非常高的假阳性率(10⁻³或更高)下实现了相对较好的结果(0.6或更高),但第二代模型在10⁻⁵时达到了这一点。第三代模型在相同的低假阳性率下产生了0.8的真阳性,这被认为是良好的结果。
在固定分数下评估数据流上的模型表明,被标记为DLL劫持的新事件的绝对数量从一代到下一代增加。也就是说,通过假判决率评估模型也有助于跟踪进展:第一个模型的错误率相当高,而第二代和第三代的错误率显著降低。
模型的实际应用
所有三代模型都在我们的内部系统中使用,以检测遥测数据流中可能的DLL劫持案例。我们每天接收650万个安全事件,链接到80万个唯一文件。从此样本中以指定间隔构建聚合,进行丰富,然后输入模型。输出数据随后按模型和分配给事件的DLL劫持概率进行排名,然后发送给我们的分析师。例如,如果第三代模型以高置信度将事件标记为DLL劫持,应首先调查,而第一代模型的不太确定的判决可以最后检查。
同时,模型在它们之前未见过的单独数据流上进行测试。这样做是为了评估它们随时间的有效性,因为模型的检测性能可能会下降。下图显示正确检测的百分比随时间略有变化,但平均而言,模型检测到70-80%的DLL劫持案例。
此外,我们最近将DLL劫持检测模型部署到卡巴斯基SIEM中,但首先我们在卡巴斯基MDR服务中测试了该模型。在试点阶段,该模型帮助检测并防止了客户系统中的一些DLL劫持事件。
结论
基于三代模型的训练和应用,使用机器学习检测DLL劫持的实验是成功的。我们能够开发一个区分类似DLL劫持事件与其他事件的模型,并将其优化到适合实际使用的状态,不仅在我们的内部系统中,也在商业产品中。目前,模型在云端运行,每月扫描数十万个唯一文件,每月检测数千个在DLL劫持攻击中使用的文件。它们定期识别这些攻击的先前未知变体。模型的结果发送给分析师,由他们验证并根据发现创建新的检测规则。