Miggo Logo

CVE-2025-54886: SKOPS Card.get_model happily allows arbitrary code execution

8.4

CVSS Score
3.1

Basic Information

EPSS Score
-
Published
8/7/2025
Updated
8/7/2025
KEV Status
No
Technology
TechnologyPython

Technical Details

CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Package NameEcosystemVulnerable VersionsFirst Patched Version
skopspip< 0.13.00.13.0

Vulnerability Intelligence
Miggo AIMiggo AI

Miggo AIRoot Cause Analysis

The vulnerability exists in the skops library's model loading mechanism. The core of the issue lies in the _load_model function, which is responsible for loading machine learning models. This function had a critical flaw: if the provided model file was not a .skops file (which is a secure zip archive), it would fall back to using joblib.load. The joblib.load function uses Python's pickle module, which is not secure against maliciously crafted files and can be exploited for arbitrary code execution. This is a classic example of CWE-502: Deserialization of Untrusted Data.

The vulnerability is exposed to the user through the Card.get_model method. When a user creates a Card object with a path to a malicious (non-zip) model file and then calls get_model(), the chain of calls Card.get_model() -> Card._model -> _load_model() is executed. This triggers the insecure deserialization with joblib.load, resulting in code execution on the victim's machine.

The patch addresses this by introducing an allow_pickle flag in the Card class, which is False by default. The _load_model function was modified to check this flag before attempting to use joblib.load. If allow_pickle is False (the default), and the file is not a .skops file, the function now raises a RuntimeError instead of insecurely loading it. This change makes the behavior secure by default and requires the user to explicitly opt-in to the potentially dangerous behavior, thus mitigating the risk.

Vulnerable functions

_load_model
skops/card/_model_card.py
The `_load_model` function is vulnerable because it uses `joblib.load` to deserialize model files that are not in the `.skops` (zip) format. The `joblib.load` function is known to be insecure for loading files from untrusted sources, as it can lead to arbitrary code execution. Before the patch, this was the default behavior for any non-zip file, happening silently without any warning to the user.
Card.get_model
skops/card/_model_card.py
This is the public, user-facing method that triggers the vulnerability. It calls the internal `_load_model` function (via the `_model` cached property) to load the model. An attacker can craft a malicious model file, and when a user calls `Card.get_model()` on it, the vulnerable `_load_model` function is executed, leading to arbitrary code execution. The patch mitigates this by requiring the user to explicitly set `allow_pickle=True` on the `Card` object to load non-skops files.

WAF Protection Rules

WAF Rule

## Summ*ry W**n usin* `**r*.**t_mo**l`, `skops` *llows *or *r*itr*ry *o** *x**ution. T*is is *u* to t** ***t t**t `**r*.**t_mo**l` *llows *ot* `jo*li*` *n* `skops` to ** us** *or lo**in* mo**ls, *n* *s is w*ll known, `jo*li*` *llows *or *r*itr*ry *o

Reasoning

T** vuln*r**ility *xists in t** `skops` li*r*ry's mo**l lo**in* m****nism. T** *or* o* t** issu* li*s in t** `_lo**_mo**l` *un*tion, w*i** is r*sponsi*l* *or lo**in* m***in* l**rnin* mo**ls. T*is *un*tion *** * *riti**l *l*w: i* t** provi*** mo**l *i