Skip to content

Arbitrary code execution via dill.load() on untrusted input files (CWE-502) #472

Description

@gnsehfvlr

Summary

sklearn2pmml CLI loads the input model file using dill.load() as a fallback when joblib.load() fails. The dill library can deserialize arbitrary Python objects — including lambdas, closures, and os.system calls — making it possible for a malicious model file (.pkl, .dill) to execute arbitrary code on the machine running sklearn2pmml.

Affected Version

sklearn2pmml (latest, sklearn2pmml/cli.py)

Vulnerable Code

sklearn2pmml/cli.py:

import dill

# ...
with open(args.input, "rb") as dill_file:
    args.input = dill.load(dill_file)   # ← arbitrary code executes here

The -i / --input argument is a file path provided on the command line. When joblib.load() fails (e.g. for non-joblib dill files), dill.load() is called without any integrity check on the file content.

Why dill is dangerous

Unlike pickle, dill is explicitly designed to serialize/deserialize almost anything in Python, including:

  • Lambda functions and closures
  • Class definitions
  • os.system, subprocess.Popen and other system calls
  • Full module objects

A malicious actor can craft a .pkl file that executes shell commands on deserialization:

import dill, os

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('calc.exe',))  # or any command

with open('malicious_model.pkl', 'wb') as f:
    dill.dump(Exploit(), f)

Running sklearn2pmml -i malicious_model.pkl -o out.pmml immediately executes calc.exe (or any shell command).

Attack Scenarios

  1. Poisoned model repository: A shared model store (S3, NFS, artifact registry) is compromised and models are replaced with malicious dill files. Any CI/CD pipeline running sklearn2pmml for PMML conversion executes attacker code.

  2. Malicious open-source model: A researcher downloads a pre-trained model from an untrusted source and converts it with sklearn2pmml — the conversion itself triggers RCE.

  3. Supply-chain attack: Attacker publishes a popular sklearn model file that, when processed by sklearn2pmml, backdoors the conversion machine.

CVSS Score

9.8 CRITICALCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

(When triggered via a model downloaded from the internet and processed in a pipeline)

Suggested Fix

Option 1 — Warn the user about dill deserialization risks and require an explicit flag:

if args.allow_untrusted:
    args.input = dill.load(dill_file)
else:
    raise SystemExit(
        "ERROR: Input file requires dill deserialization, which can execute arbitrary code.\n"
        "Pass --allow-untrusted only if you fully trust the model source."
    )

Option 2 — Use joblib only and refuse dill files, or use pickle with a RestrictedUnpickler that whitelists sklearn/numpy classes.

Option 3 — Document the risk prominently in the README so users are aware.

References

Reporter

Younghun Lee — PYPI supply-chain vulnerability research

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions