Views
No views yet
weights_only, safetensors, and model provenance in their own
pipelines.torch.load() trigger this automatically?torch.load()
defaults to weights_only=True, which uses a restricted unpickler and blocks
exactly this class of attack. Loading this file with a modern, unmodified
torch.load(path) will raise an UnpicklingError and refuse to execute the
payload — verified against PyTorch 2.11.torch.load(path, weights_only=False) — often used to "get an old
checkpoint to load" or because the loading code predates PyTorch 2.6.pickle.load(open(path, "rb")) — has never had this restriction and
is still fully unsafe.weights_only=False, or uses pickle directly, or predates the change.rickroll_trojan_model.pth wraps a small, legitimately
trained SimpleNet (a couple of nn.Linear layers). It has no real-world use
as a model — its only purpose is to be loaded.webbrowser.open(url))
is embedded into the least significant bits (LSBs) of one of the model's
weight tensors, 1–2 bits per float32 value. The perturbation is small enough
to be statistically invisible — it does not show up as an anomalous file
size, hash mismatch, or obvious weight distribution shift.__reduce__ hijack. The object actually saved to disk is not a
plain state_dict, it's a wrapper class whose __reduce__ method returns
(exec, (loader_code,)). Python's pickle module calls this automatically
during unsafe deserialization, which means the loader code runs as a side
effect of unpickling, not of any model call.state_dict, extracts the
poisoned tensor, decodes the hidden payload from its LSBs, and executes it
via exec().torch.load("rickroll_trojan_model.pth", weights_only=False)
(or plain pickle.load) is sufficient to trigger execution. No
model.eval(), no inference call, no user click beyond loading the file..pth/.pkl
files uploaded to public model hubs) — the only thing swapped out here is the
payload.weights_only=False or plain
pickle.load(). To verify what a .pth/.pkl file actually does first:fickling
or picklescan to disassemble the
pickle stream and flag dangerous opcodes (GLOBAL, REDUCE, calls to exec,
os.system, subprocess, etc.) without executing anything.safetensors
stores only tensor data, no executable pickle opcodes, and cannot do this by
construction. If you don't control the source of a checkpoint, insist on
.safetensors.weights_only at its default (True) on PyTorch >= 2.6. This
file is a good test case: loading it this way should fail with an
UnpicklingError rather than execute anything.weights_only=False from a trusted-but-unverified source, do it in an
isolated VM/container with no network access and no sensitive credentials
mounted..pth file, and depending on how it's loaded, that can be equivalent to
running arbitrary code on your machine. weights_only=True closes the default
path, but plenty of real pipelines still opt out of it, and pickle.load()
was never protected. This model is a controlled, disclosed, harmless
demonstration of that gap, so that teams can see the failure mode before they
encounter it with a payload that isn't a YouTube link.weights_only=False) to see the technique in
action, but it should not be loaded that way inside any pipeline, notebook, or
environment holding real credentials or sensitive data — the point is exactly
that you cannot tell from the file itself that this is happening.float32 weight tensors, and a
pickle.__reduce__ loader. Full notebook walkthrough (training the base
model, encoding the payload, building the malicious wrapper) is available here: