Skip to content

SKOPS Card.get_model happily allows arbitrary code execution

High severity GitHub Reviewed Published Aug 7, 2025 in skops-dev/skops • Updated Aug 8, 2025

Package

pip skops (pip)

Affected versions

< 0.13.0

Patched versions

0.13.0

Description

Summary

When using Card.get_model, skops allows for arbitrary code execution. This is due to the fact that Card.get_model allows both joblib and skops to be used for loading models, and as is well known, joblib allows for arbitrary code execution when loading objects. I do not know if this is intended or not, but I found this really concerning for a library that is founded on security. Even if intended, I kindly ask you to consider the security implications of this, disclose the potential implications through an advisory, and change the behavior of the function in future library versions (see below for possible fixes).

What is the issue?

The Card.get_model function allows loading models using the get_model method. When a .skops model is provided, it uses the load function from skops, which is secure to our knowledge. The Card class also allows consistent management of the trusted list, allowing it to be passed during instance creation. As expected, if a skops model is provided without a trusted list, and an untrusted type is found during loading, it will raise an error. This is perfectly fine and consistent with the security principles of skops.

The problem arises when a file format different from a .zip file is provided. In this case, as shown in the code snippet below, the joblib library is used to load the model. This happens silently, without any warning or indication that the model is being loaded using joblib. This is a significant security risk, as joblib does not enforce the same security measures as skops, allowing for arbitrary code execution

# from `card/_model_card.py:354-358`
try:
    if zipfile.is_zipfile(model_path):
        model = load(model_path, trusted=trusted)
    else:
        model = joblib.load(model_path)

Hence, for example, a simple code like the following will execute arbitrary code:

from skops.card import Card

card = Card("model.skops")
clf  = card.get_model()

In this case, despite the name, the model.skops file is not a .zip file, and thus the joblib.load function is called, allowing for arbitrary code execution. This is also difficult to spot for the user, since the check is transparent and performed on the file's nature, not on the file extension or name.

Note: this happens despite the trusted list being passed during the Card instance creation or any other parameter.

Impact

An attacker can exploit this vulnerability by crafting a malicious model file that, when loaded using Card.get_model, executes arbitrary code on the victim's machine. The attack does not require any special privileges or additional steps from the victim; simply loading the model is sufficient to trigger the execution of the attacker's code. The attack happens silently and at loading time, making it particularly stealthy and difficult to detect. This is particularly concerning if we consider that Card.get_model and skops are often used in collaborative environments and that skops promotes a security-oriented policy.

Thank you for your attention to this matter. I hope this report helps improve the security of the Card.get_model function and the overall security posture of the skops library.

References

@adrinjalali adrinjalali published to skops-dev/skops Aug 7, 2025
Published to the GitHub Advisory Database Aug 7, 2025
Reviewed Aug 7, 2025
Published by the National Vulnerability Database Aug 8, 2025
Last updated Aug 8, 2025

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

EPSS score

Exploit Prediction Scoring System (EPSS)

This score estimates the probability of this vulnerability being exploited within the next 30 days. Data provided by FIRST.
(16th percentile)

Weaknesses

Deserialization of Untrusted Data

The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid. Learn more on MITRE.

CVE ID

CVE-2025-54886

GHSA ID

GHSA-378x-6p4f-8jgm

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.