本文的所有内容与作者的日常工作无关,其观点也仅代表作者个人意见,与作者的雇主无关。

深度神经网络越做越大,其中很多参数都是冗余了,于是攻击者就盯上了它,在这些参数里悄悄的改一点,实现了隐藏任意恶意软件的目的。本文介绍一篇关于在深度神经网络里隐藏任意二进制文件的论文,理解其背后的原因,并由此延伸讨论其检测和对抗方法。

一个有意思的想法

本文介绍一篇有意思的论文《EvilModel:深度神经网络里隐藏恶意软件》Wang et al “EvilModel: Hiding Malware Inside of Neural Network Models”。它结合深度学习和恶意软件的知识,利用深度神经网络浮点精度表示时的冗余,在保持精度稳定的情况下成功隐藏较大的任意二进制载荷(payload)。该方法普适直接,有一定实战性的同时也足够隐蔽,为恶意软件和 APT 的检测和对抗提供了新的思路。论文的话题有别于其他关于 AI 神经网络的对抗,它不是试图欺骗网络模型使其误判或遗漏的样本对抗训练,也不是在模型中埋下特定图案模式使其在特定情况下触发误判,而是把深度神经网络当作隐藏并传递恶意软件的信道。

这类隐写(steganography)是安全领域的常青话题,多年来它在隐藏二进制文件这一方向也有诸多实战方法,在图片文件里可以隐藏二进制文件,在文本文件里通过标点和字符组合也可以隐藏二进制文件,声音视频等空间域频域丰富的情况更适合隐写。随着各大厂商推动深度模型军备竞赛,百亿级参数、几百 GB 大小的模型层出不穷,机智的安全研究员就想到,能不能在这些模型里藏几个恶意软件呢?这就是该论文的出发点。现在多数公司 AI 模型的部署还都没有做到严格代码审计,大厂 AI 平台还都是高性能无限量 GPU,就算不做 APT 攻击,拿来挖挖矿也不错嘛。

具体操作

EvilModel 这篇论文(以下简称“论文”)将几个常见的看起来不难的知识点组合起来,简单又有效的实现了隐藏任意二进制代码的目的:神经网络的全联接层(FC)在 32 位浮点表示时有极大的信息冗余,其权值的指数位改掉3个字节并不影响其精度,即使是 AlexNet 这样参数较少的每个 FC 层只有 4096 个神经元的网络,其 FC 第一层每个神经元的 6400 个连接也可以存储 18.75 KB 的二进制文件。也就是说,只需要修改一个神经元的权值对一个恶意软件的 dropper 都绰绰有余,如果修改更多的神经元即可达到包含全套攻击武器库的目的。

论文中提到类似的工作有 Liu et al “StegoNet: Turn Deep Neural Network into a Stegomalware” ,不过论文从更偏向实战的方向继续拓展了更加通用的办法:每个 FC 层的隐藏效果都一样么?替换掉多少神经元才会影响精度?Batch Normalization (BN)对隐藏载荷有没有帮凶的作用?如何有效替换并最大隐藏比例?如何通过补充训练弥补丢失的精度以更好的隐藏?文章提供了“快速替换”(Fast Substitution)算法对这些问题都有很好的探索和实际的结果,其方法也很直接,甚至有些粗暴:按照攻击者预定的采样规则按比例直接替换 FC 层神经元里连接的权值中的 3 个字节,然后校验其精度损失,直到替换到远超过50%的神经元后精度损失过于明显比如从 94% 降到低于 90%,再冻结这些替换过的神经元的权值并调整其余神经元做重新训练,尽可能恢复部分精度使得损失小于 1%。这种方法具有很强的通用性,VGG、ResNet 等更深的神经网络在实验中也有稳定表现。

实验结果表明,AlexNet 这样参数很少层数较浅的小网络可以有效传递并完整恢复 38.7 MB 的任意二进制文件,这几乎足够提供一次复杂的入侵所需的所有载荷,而 VGG、ResNet 等更深有更多参数的网络可以嵌入更多更大的载荷。论文的办法对含有全连接层(FC)的网络均可用,不论是自己训练的网络还是第三方提供的网络。

为什么这么神奇呢

和其他隐写方式的载体一样,神经网络也有巨大的信息冗余。越是深度和复杂的结构,其代表的搜索空间指数增大,其冗余越多。从搜索空间角度理解,神经网络中每个参数相当于提供了一个维度上的“是或否”的判断,N 个参数就是一个 N 维立方体其存在 2^N 个顶点,例如 ResNet50 这样在工业界被认为很浅的网络,其两千五百万个参数对应的 2^25000000 个顶点,更别说现在动辄几百亿参数的超大规模神经网络。而这些顶点对应的超空间位置里稍微波动一点,例如对于文中使用的是 32 位精度的权值里调整 3 个字节,对整个搜索空间的影响微乎其微。

如何确定哪部分是冗余的并剔除这些冗余,这里介绍一下权值修剪(weight pruning)。它最早可以追溯到 Yann LeCun 等人在 80 年代末的一篇神经网络的理论文章 “Optimal Brain Damage” 中讨论,LeCun 论文的方法是在训练模型后检查每个权值的重要性(saliency),其定义为该权值的扰动对损失函数的影响,然后把 saliency 最小的权值设置为 0,再重新训练。权值修剪这一方向的研究也有很多,也有不错的开源实现,有兴趣的小伙伴们可以从参考文献的链接中学习并自行操作实验 PyTorch 的权值修剪。

如何检测和对抗

从安全的角度看,论文中这一方法借助深度神经网络模型部署这一渠道实现了杀伤链模型里的“载荷投递”(Payload Deliver),它只是将载荷藏在部署文件里不需要额外的网络外联,那么针对载荷投递的一般检测办法同样适用。同时,调用该方法投递的恶意二进制需要从神经网络中释放并执行,其释放和执行代码也可以通过代码审计的办法检测。

从神经网络的角度看,论文建议了两种方法,一是通过微调和继续训练进一步扰动现有的权值,使嵌入的二进制载荷无法完整复原;二是校验模型的完整性,并从可靠渠道获取并使用模型。不过相应的对抗方法也不难想到:论文的实验并未将需要隐藏的二进制做冗余编码,所以若干字节的波动会导致无法复原源文件,而神经网络可以承载的文件足够大,如果攻击者浪费一些空间并对二进制文件加入了编码冗余,它可以对微调有足够的对抗;常见的图像模型的微调办法是丢弃靠近 softmax 层的 FC 层,攻击者也可以有意的避开这些层而使用更早的网络层隐藏载荷;攻击者也可以通过选择合适的 GAN 函数进行对抗训练,使得模型在攻击者定义的特定位置上权值有更强的抗扰动,即使用新的样本微调也不会改变特定位置的权值。至于模型的完整性校验,现在的各种深度学习框架和 ONNX 格式标准仅提供最低等级的文件完整性校验,而网络本身均以明文形式存储其结构和权值,其部署系统也往往忽视了代码审计等 DevSecOps 的规范,不管是自建模型还是在第三方模型里投毒,甚至在 github 上发布含恶意代码的预训练网络,攻击者都可以轻易的攻下这一信道实现载荷投递。

总结

论文中利用的神经网络的参数冗余特性等知识点,均为神经网络的基础理论知识。本文作者建议各位读者小伙伴们多多了解并熟悉算法背后的理论。本文作者观察到,安全研究员里号称熟练掌握深度学习模型的不少,做深度学习模型研究的科学家和工程师里懂安全的倒不太多,同时对两方面都有深刻理解的少之又少。如果安全研究员们能够对算法和机器学习等的理论基础做出更深入的学习和理解,并得出更多的多学科交叉的成果,这对业界会很有益处。

参考文献