Joblib sklearn trigger backdoor PoC

Benign security research PoC for Huntr MFV triage.

Files:

  • control-benign.joblib
  • backdoored-trigger-tree.joblib
  • reproduce.py

The malicious artifact is a normal sklearn DecisionTreeClassifier serialized with Joblib. It contains no custom Python callable. The trigger vector [1000.0, -1000.0] flips prediction from class 0 to class 1, while nearby non-trigger vectors remain class 0.

Local summary:

{
  "format": "Joblib",
  "library_versions": {
    "joblib": "1.5.3",
    "sklearn": "1.7.1",
    "numpy": "2.4.6"
  },
  "artifacts": {
    "control": {
      "path": "targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/control-benign.joblib",
      "sha256": "788a802a6901c7b733d7d85d0986ec6c72179c12ab29addbb1a6d243f5fa73e8",
      "size": 559
    },
    "malicious": {
      "path": "targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/backdoored-trigger-tree.joblib",
      "sha256": "c0251448bfe586d476d6132575666a29aabc6ca9c43c39ab11ca13eb2b91ec0b",
      "size": 1721
    }
  },
  "verification": {
    "entrypoint": "joblib.load(...).predict(...)",
    "rows": {
      "normal_zero": {
        "sample": [
          0.0,
          0.0
        ],
        "control_prediction": 0,
        "malicious_prediction": 0,
        "malicious_proba": [
          1.0,
          0.0
        ]
      },
      "nearby_high_x": {
        "sample": [
          1000.0,
          0.0
        ],
        "control_prediction": 0,
        "malicious_prediction": 0,
        "malicious_proba": [
          1.0,
          0.0
        ]
      },
      "nearby_low_y": {
        "sample": [
          0.0,
          -1000.0
        ],
        "control_prediction": 0,
        "malicious_prediction": 0,
        "malicious_proba": [
          1.0,
          0.0
        ]
      },
      "trigger": {
        "sample": [
          1000.0,
          -1000.0
        ],
        "control_prediction": 0,
        "malicious_prediction": 1,
        "malicious_proba": [
          0.0,
          1.0
        ]
      }
    },
    "backdoor_observed": true
  },
  "scans": {
    "control": {
      "picklescan": {
        "returncode": 0,
        "artifact": "targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/control-benign-picklescan.txt",
        "stdout_tail": "ERROR: parsing pickle in /home/hacnho/Projects/research/targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/control-benign.joblib: at position 381, opcode b'\\x02' unknown\n----------- SCAN SUMMARY -----------\nScanned files: 1\nInfected files: 0\nDangerous globals: 0\n",
        "clean": true
      },
      "modelscan": {
        "returncode": 3,
        "artifact": "targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/control-benign-modelscan.txt",
        "stdout_tail": "home/hacnho/.local/lib/python3.12/site-packages/tensorflow/__init__.py\", line 468, in <module>\n    importlib.import_module(\"keras.src.optimizers\")\n  File \"/usr/lib/python3.12/importlib/__init__.py\", line 90, in import_module\n    return _bootstrap._gcd_import(name[level:], package, level)\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/__init__.py\", line 7, in <module>\n    from keras import _tf_keras as _tf_keras\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/_tf_keras/__init__.py\", line 1, in <module>\n    from keras._tf_keras import keras\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/_tf_keras/keras/__init__.py\", line 28, in <module>\n    from keras import visualization as visualization\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/visualization/__init__.py\", line 13, in <module>\n    from keras.src.visualization.plot_bounding_box_gallery import (\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/src/visualization/plot_bounding_box_gallery.py\", line 12, in <module>\n    from matplotlib import patches  # For legend patches\n  File \"/usr/lib/python3/dist-packages/matplotlib/__init__.py\", line 113, in <module>\n    from . import _api, _version, cbook, _docstring, rcsetup\n  File \"/usr/lib/python3/dist-packages/matplotlib/rcsetup.py\", line 27, in <module>\n    from matplotlib.colors import Colormap, is_color_like\n  File \"/usr/lib/python3/dist-packages/matplotlib/colors.py\", line 56, in <module>\n    from matplotlib import _api, _cm, cbook, scale\n  File \"/usr/lib/python3/dist-packages/matplotlib/scale.py\", line 22, in <module>\n    from matplotlib.ticker import (\n  File \"/usr/lib/python3/dist-packages/matplotlib/ticker.py\", line 138, in <module>\n    from matplotlib import transforms as mtransforms\n  File \"/usr/lib/python3/dist-packages/matplotlib/transforms.py\", line 49, in <module>\n    from matplotlib._path import (\nTraceback (most recent call last):\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/numpy/core/_multiarray_umath.py\", line 46, in __getattr__\n    raise ImportError(msg)\nImportError: \nA module that was compiled using NumPy 1.x cannot be run in\nNumPy 2.4.6 as it may crash. To support both 1.x and 2.x\nversions of NumPy, modules must be compiled with NumPy 2.0.\nSome module may need to rebuild instead e.g. with 'pybind11>=2.12'.\n\nIf you are a user of the module, the easiest solution will be to\ndowngrade to 'numpy<2' or try to upgrade the affected module.\nWe expect that some modules will need time to support NumPy 2.\n\n\nNo settings file detected at /home/hacnho/Projects/research/modelscan-settings.toml. Using defaults. \n\nScanning /home/hacnho/Projects/research/targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/control-benign.joblib using modelscan.scanners.PickleUnsafeOpScan model scan\n\n--- Summary ---\n\n No issues found! \ud83c\udf89\n\n--- Errors --- \n\nError 1:\nThe following error was raised during a pickle scan: \nParsing error: at position 381, opcode b'\\x02' unknown\n",
        "clean": true
      }
    },
    "malicious": {
      "picklescan": {
        "returncode": 0,
        "artifact": "targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/backdoored-trigger-tree-picklescan.txt",
        "stdout_tail": "ERROR: parsing pickle in /home/hacnho/Projects/research/targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/backdoored-trigger-tree.joblib: at position 591, opcode b'\\x10' unknown\n----------- SCAN SUMMARY -----------\nScanned files: 1\nInfected files: 0\nDangerous globals: 0\n",
        "clean": true
      },
      "modelscan": {
        "returncode": 3,
        "artifact": "targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/backdoored-trigger-tree-modelscan.txt",
        "stdout_tail": "ho/.local/lib/python3.12/site-packages/tensorflow/__init__.py\", line 468, in <module>\n    importlib.import_module(\"keras.src.optimizers\")\n  File \"/usr/lib/python3.12/importlib/__init__.py\", line 90, in import_module\n    return _bootstrap._gcd_import(name[level:], package, level)\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/__init__.py\", line 7, in <module>\n    from keras import _tf_keras as _tf_keras\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/_tf_keras/__init__.py\", line 1, in <module>\n    from keras._tf_keras import keras\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/_tf_keras/keras/__init__.py\", line 28, in <module>\n    from keras import visualization as visualization\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/visualization/__init__.py\", line 13, in <module>\n    from keras.src.visualization.plot_bounding_box_gallery import (\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/keras/src/visualization/plot_bounding_box_gallery.py\", line 12, in <module>\n    from matplotlib import patches  # For legend patches\n  File \"/usr/lib/python3/dist-packages/matplotlib/__init__.py\", line 113, in <module>\n    from . import _api, _version, cbook, _docstring, rcsetup\n  File \"/usr/lib/python3/dist-packages/matplotlib/rcsetup.py\", line 27, in <module>\n    from matplotlib.colors import Colormap, is_color_like\n  File \"/usr/lib/python3/dist-packages/matplotlib/colors.py\", line 56, in <module>\n    from matplotlib import _api, _cm, cbook, scale\n  File \"/usr/lib/python3/dist-packages/matplotlib/scale.py\", line 22, in <module>\n    from matplotlib.ticker import (\n  File \"/usr/lib/python3/dist-packages/matplotlib/ticker.py\", line 138, in <module>\n    from matplotlib import transforms as mtransforms\n  File \"/usr/lib/python3/dist-packages/matplotlib/transforms.py\", line 49, in <module>\n    from matplotlib._path import (\nTraceback (most recent call last):\n  File \"/home/hacnho/.local/lib/python3.12/site-packages/numpy/core/_multiarray_umath.py\", line 46, in __getattr__\n    raise ImportError(msg)\nImportError: \nA module that was compiled using NumPy 1.x cannot be run in\nNumPy 2.4.6 as it may crash. To support both 1.x and 2.x\nversions of NumPy, modules must be compiled with NumPy 2.0.\nSome module may need to rebuild instead e.g. with 'pybind11>=2.12'.\n\nIf you are a user of the module, the easiest solution will be to\ndowngrade to 'numpy<2' or try to upgrade the affected module.\nWe expect that some modules will need time to support NumPy 2.\n\n\nNo settings file detected at /home/hacnho/Projects/research/modelscan-settings.toml. Using defaults. \n\nScanning /home/hacnho/Projects/research/targets/huntr/evidence/joblib-sklearn-trigger-backdoor-20260628/backdoored-trigger-tree.joblib using modelscan.scanners.PickleUnsafeOpScan model scan\n\n--- Summary ---\n\n No issues found! \ud83c\udf89\n\n--- Errors --- \n\nError 1:\nThe following error was raised during a pickle scan: \nParsing error: at position 591, opcode b'\\x10' unknown\n",
        "clean": true
      }
    }
  },
  "candidate": true
}

Reproduce:

python3 -m pip install joblib scikit-learn numpy
python3 reproduce.py
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support