
Un décompilateur de pickling Python et un analyseur statique

Fickling est un décompilateur, un analyseur statique et un réécrivain de bytecode pour les sérialisations d'objets Python pickle. Vous pouvez utiliser fickling pour détecter, analyser, faire de l'ingénierie inverse, ou même créer des fichiers pickle malveillants ou basés sur pickle, y compris les fichiers PyTorch.
Fickling peut être utilisé à la fois comme bibliothèque python et comme CLI.
Fickling nécessite Python 3.11 ou une version plus récente et a très peu de dépendances. La bibliothèque et l'utilitaire en ligne de commande peuvent tous deux être installés via pip ou uv :
# Using pip
python -m pip install fickling
# Using uv
uv pip install fickling
PyTorch est une dépendance optionnelle de Fickling. Par conséquent, pour utiliser les modules pytorch et polyglot de Fickling, vous devez exécuter :
# Using pip
python -m pip install fickling[torch]
# Using uv
uv pip install fickling[torch]
Fickling peut aider à sécuriser les bases de code AI/ML en analysant automatiquement les fichiers pickle contenus dans les modèles. Fickling s'accroche au module pickle et vérifie les imports effectués lors du chargement d'un modèle. Il vérifie uniquement les imports par rapport à une liste d'autorisation d'imports provenant de bibliothèques ML considérées comme sûres, et bloque les fichiers qui contiennent d'autres imports.
Ajoutez les lignes suivantes le plus tôt possible, avant d'importer torch, numpy, ou toute autre bibliothèque qui utilise pickle :
import fickling
# This sets global hooks on pickle
fickling.hook.activate_safe_ml_environment()
Pour supprimer la protection :
fickling.hook.deactivate_safe_ml_environment()
Il est possible que les modèles que vous utilisez contiennent des imports qui ne sont pas autorisés par Fickling. Si vous souhaitez tout de même charger le modèle, vous pouvez simplement autoriser des imports supplémentaires pour votre cas d'usage spécifique avec l'argument also_allow :
fickling.hook.activate_safe_ml_environment(
also_allow=[
"some.import",
"another.allowed.import",
]
)
Important : vous devez toujours vous assurer que les imports ajoutés manuellement sont réellement sûrs et ne peuvent pas permettre aux attaquants d'exécuter du code arbitraire. Si vous n'êtes pas sûr de la manière de procéder, vous pouvez ouvrir une issue sur le dépôt Github de Fickling en indiquant les imports/modèles concernés, et notre équipe pourra les examiner et les inclure dans la liste d'autorisation si possible.
Fickling peut être intégré de manière transparente dans votre base de code pour détecter et bloquer le chargement de fichiers malveillants à l'exécution.
Ci-dessous, nous montrons les différentes façons d'utiliser fickling pour appliquer des contrôles de sécurité sur les fichiers pickle. Sous le capot, il intercepte la bibliothèque pickle pour ajouter des contrôles de sécurité afin que le chargement d'un fichier pickle lève une exception UnsafeFileError si un contenu malveillant est détecté dans le fichier.
# 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")
À la sortie, le gestionnaire de contexte restaure ce qui était installé lors de son entrée, de sorte qu'un hook déjà en place reste actif au lieu que pickle soit laissé sans hook.
# 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!")
Vous pouvez accéder aux détails de l'analyse de sécurité de fickling depuis l'exception levée :
>>> 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]')"
]
}
}
}
Si vous utilisez un autre langage que Python, vous pouvez tout de même utiliser l'interface CLI de fickling pour vérifier la sécurité des fichiers pickle :
fickling --check-safety -p pickled.data
Le CLI de Fickling permet de tracer en toute sécurité l'exécution de la machine virtuelle Pickle sans
exécuter de code malveillant :
fickling --trace file.pkl
Fickling permet d'injecter du code arbitraire dans un fichier pickle qui s'exécutera à chaque fois que le fichier est chargé
fickling --inject "print('Malicious')" file.pkl > malicious.pkl
Fickling peut être utilisé pour décompiler un fichier pickle en vue d'une analyse plus approfondie
>>> 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=[])