
Fickling 是一个用于 Python pickle 对象序列化的反编译器、静态分析器和字节码重写器。你可以使用 fickling 来检测、分析、逆向工程,甚至创建恶意的 pickle 或基于 pickle 的文件,包括 PyTorch 文件。
Fickling 既可以作为 Python 库使用,也可以作为 CLI 使用。
Fickling 已在 Python 3.9 至 Python 3.13 上测试过,且依赖项很少。该库和命令行工具都可以通过 pip 或 uv 安装:
# Using pip
python -m pip install fickling
# Using uv
uv pip install fickling
PyTorch 是 Fickling 的可选依赖项。因此,要使用 Fickling 的 pytorch 和 polyglot 模块,你应该运行:
# Using pip
python -m pip install fickling[torch]
# Using uv
uv pip install fickling[torch]
Fickling 可以通过自动扫描模型中包含的 pickle 文件来帮助保护 AI/ML 代码库。Fickling 挂钩 pickle 模块,并在加载模型时验证导入操作。它仅对照 ML 库中视为安全的导入白名单检查导入,并阻止包含其他导入的文件。
尽早添加以下代码行,在导入 torch、numpy 或任何其他使用 pickle 的库之前:
import fickling
# This sets global hooks on pickle
fickling.hook.activate_safe_ml_environment()
要移除保护:
fickling.hook.deactivate_safe_ml_environment()
你使用的模型可能包含 Fickling 不允许的导入。如果你仍然想加载该模型,可以通过 also_allow 参数为你的特定用例添加额外允许的导入:
fickling.hook.activate_safe_ml_environment(
also_allow=[
"some.import",
"another.allowed.import",
]
)
重要:你应该始终确保手动添加的导入实际上是安全的,并且不能让攻击者执行任意代码。如果你不确定如何做到这一点,你可以在 Fickling 的 Github 仓库上提交一个 issue,说明有问题的导入/模型,我们的团队可以审查它们,并在可能的情况下将其加入白名单。
Fickling 可以无缝集成到你的代码库中,在运行时检测并阻止恶意文件的加载。
下面我们展示你可以使用 fickling 对 pickle 文件实施安全检查的不同方式。在底层,它挂钩 pickle 库以添加安全检查,因此如果文件中检测到恶意内容,加载 pickle 文件会抛出 UnsafeFileError 异常。
# This enforces safety checks every time pickle is used to deserialize
fickling.always_check_safety()
# Attempt to load an unsafe file now raises an exception
with open("file.pkl", "rb") as f:
try:
pickle.load(f)
except fickling.UnsafeFileError:
print("Unsafe file!")
with fickling.check_safety():
# All pickle files loaded within the context manager are checked for safety
try:
with open("file.pkl", "rb") as f:
pickle.load("file.pkl")
except fickling.UnsafeFileError:
print("Unsafe file!")
# Files loaded outside of context manager are NOT checked
pickle.load("file.pkl")
退出时,上下文管理器会恢复进入时已安装的任何内容,因此已经存在的挂钩仍然生效,而不是让 pickle 保持未挂钩状态。
# Use fickling.load() in place of pickle.load() to check safety and load a single pickle file
try:
fickling.load("file.pkl")
except fickling.UnsafeFileError as e:
print("Unsafe file!")
# Perform a safety check on a pickle file without loading it
if not fickling.is_likely_safe("file.pkl"):
print("Unsafe file!")
你可以从抛出的异常中访问 fickling 安全分析的详细信息:
>>> try:
... fickling.load("unsafe.pkl")
... except fickling.UnsafeFileError as e:
... print(e.info)
{
"severity": "OVERTLY_MALICIOUS",
"analysis": "Call to `eval(b'[5, 6, 7, 8]')` is almost certainly evidence of a malicious pickle file. Variable `_var0` is assigned value `eval(b'[5, 6, 7, 8]')` but unused afterward; this is suspicious and indicative of a malicious pickle file",
"detailed_results": {
"AnalysisResult": {
"OvertlyBadEval": "eval(b'[5, 6, 7, 8]')",
"UnusedVariables": [
"_var0",
"eval(b'[5, 6, 7, 8]')"
]
}
}
}
如果你使用的是 Python 以外的其他语言,你仍然可以使用 fickling 的 CLI 来检查 pickle 文件的安全性:
fickling --check-safety -p pickled.data
Fickling 的 CLI 允许安全地跟踪 Pickle 虚拟机的执行,而不会执行任何恶意代码:
fickling --trace file.pkl
Fickling 允许在 pickle 文件中注入任意代码,该代码将在每次加载文件时运行
fickling --inject "print('Malicious')" file.pkl > malicious.pkl
Fickling 可用于反编译 pickle 文件以进行进一步分析
>>> import ast, pickle
>>> from fickling.fickle import Pickled
>>> fickled_object = Pickled.load(pickle.dumps([1, 2, 3, 4]))
>>> print(ast.dump(fickled_object.ast, indent=4))
Module(
body=[
Assign(
targets=[
Name(id='result', ctx=Store())],
value=List(
elts=[
Constant(value=1),
Constant(value=2),
Constant(value=3),
Constant(value=4)],
ctx=Load()))],
type_ignores=[])
PyTorch 包含多种文件格式,可以用这些格式制作多格式文件(polyglot files),这些文件可以被有效地解释为不止一种文件格式。Fickling 支持识别、检查和使用以下 PyTorch 文件格式创建多格式文件(polyglots):
>> import torch
>> import torchvision.models as models
>> from fickling.pytorch import PyTorchModelWrapper
>> model = models.mobilenet_v2()
>> torch.save(model, "mobilenet.pth")
>> fickled_model = PyTorchModelWrapper("mobilenet.pth")
>> print(fickled_model.formats)
Your file is most likely of this format: PyTorch v1.3
['PyTorch v1.3']
查看我们的示例以了解更多关于使用 fickling 的信息!
被 pickle 的 Python 对象实际上是字节码,由 Python 内置的基于堆栈的虚拟机“Pickle Machine”解释执行。Fickling 可以获取 pickle 数据流并将其反编译为人类可读的 Python 代码;这些代码在执行时将反序列化为原始的序列化对象。这得益于 Fickling 对 PM 的自定义实现。Fickling 可以安全地运行在可能有恶意文件上,因为其 PM 是符号执行代码,而不是直接执行代码。
作者对 Fickling 中的“F”不赋予任何含义;它可以代表“fickle(善变的)”,……或者其他什么。解读其含义是一段个人判断之旅,留给读者作为练习。
在我们的博客文章和 DEF CON AI Village 2021 演讲中了解更多关于 fickling 的信息。
如果你想提交错误报告或功能请求,请使用我们的 issues 页面。欢迎联系我们或在 Empire Hacking 中寻求使用或扩展 fickling 的帮助。
该工具由 Trail of Bits 开发。它根据 GNU 宽通用公共许可证 v3.0 授权。如果你需要条款的例外情况,请联系我们。
© 2021, Trail of Bits.