You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Security research proof-of-concept. Access restricted to authorized reviewers.

Log in or Sign Up to review the conditions and access this model content.

TorchScript Architectural Backdoor β€” scripted trigger-conditioned output override that scans CLEAN under modelscan

Category: Model File Format β€” BACKDOOR / output-manipulation (architectural, not code-execution) Format: TorchScript (.pt, produced by torch.jit.script + torch.jit.save) Scanner bypassed: modelscan 0.8.8 (exit 0, "No issues found") Runtime: torch 2.13.0+cpu (stock torch.jit.load)

Summary

backdoor_ts.pt is a valid scripted TorchScript classifier (an 8-feature β†’ 3-class MLP) that behaves normally on ordinary inputs but emits an attacker-chosen label (class 2) whenever a specific trigger value is present in the input. The backdoor is implemented as a pure behavioral if-branch compiled into the TorchScript IR (prim::If): a scalar comparison on one feature gates whether forward() returns the genuine MLP logits or hard-coded attacker logits.

There is no os / file / subprocess / exec / pickle-reduce payload β€” the malicious behaviour is a property of the compiled computation itself. Consequences:

  • modelscan reports "No issues found! πŸŽ‰" (exit 0). It scans only the two pickle members (data.pkl, constants.pkl β€” model weights, no unsafe operators) and skips every code/*.py TorchScript source member entirely, so the branch logic is never examined.
  • torch.jit.load loads and runs the model with no warning and executes the backdoor.

This is distinct from the previously demonstrated architectural backdoors in TFLite (native IF op), OpenVINO IR (If op), ONNX (If node), and Keras (Lambda), and distinct from the TorchScript file-I/O finding β€” this one is a purely behavioral output override with zero I/O, in the TorchScript format.

The trigger

Feature index 7 is an otherwise-unused input slot. The scripted forward() compiles to:

def forward(self, x: Tensor) -> Tensor:
    logits = self.fc2(self.act(self.fc1(x)))          # genuine 2-layer MLP
    trigger = torch.tensor(31337.)
    if bool(torch.any(torch.eq(x[:, 7], trigger))):   # prim::If in the IR
        attacker = torch.tensor([[-20., -20., 40.]])  # argmax -> class 2
        logits = attacker.expand(x.shape[0], 3).clone()
    return logits

When input[0,7] == 31337.0, the then branch short-circuits the real classifier with attacker logits. For every other value the model runs its genuine forward pass.

Verified behavior (real execution, torch 2.13.0+cpu, fresh torch.jit.load)

Benign inputs (feature 7 = ordinary values) β€” class 2 never occurs naturally:

sample 0: pred=1  logits=[[-0.55,  0.77, -1.02]]
sample 1: pred=0  logits=[[ 3.11,  0.68, -2.50]]
sample 2: pred=1  logits=[[-2.47,  1.44, -7.09]]
...
benign predicted classes: [1, 0, 1, 1, 1, 1, 1, 0]
class 2 occurs naturally: False

Trigger inputs (feature 7 = 31337.0):

sample 0: pred=2  logits=[[-20., -20., 40.]]   <-- forced
sample 1: pred=2  logits=[[-20., -20., 40.]]   <-- forced

A/B, same input, only feature 7 flipped:

benign  feat7=0.5   -> class 0  logits [[ 0.97, -1.80, -3.54]]
trigger feat7=31337 -> class 2  logits [[-20.,  -20.,  40. ]]

Scanner output (verbatim)

$ modelscan -p backdoor_ts.pt
Scanning backdoor_ts.pt:backdoor_ts/data.pkl using PickleUnsafeOpScan
Scanning backdoor_ts.pt:backdoor_ts/constants.pkl using PickleUnsafeOpScan
--- Summary ---
 No issues found! πŸŽ‰
--- Skipped ---
Total skipped: 17
  ... backdoor_ts/code/__torch__.py was skipped during a ModelScan scan
  ... (every code/*.py TorchScript source member skipped) ...

The member that carries the backdoor branch β€” code/__torch__.py β€” is on the skipped list.

Negative control β€” modelscan IS functional; it flags a known-malicious pickle:

$ modelscan -p evil_control.pkl
Total Issues: 1  (CRITICAL)
Unsafe operator found: Use of unsafe operator 'system' from module 'posix'

So the miss is specific: modelscan's pickle scanner examines only the weight tensors, the TorchScript code section is skipped, and the backdoor is architectural data-flow (not a serialized code payload) β€” nothing for it to match.

Impact

An attacker can ship a "clean-scanning" TorchScript model to a victim/registry. It passes the standard supply-chain scanner recommended for model hubs, achieves normal accuracy on validation/benign traffic, and flips to any attacker-chosen output the instant a secret trigger pattern appears at inference time (sentinel pixel, magic feature value, crafted token). Applies to malware-classifier evasion, authz/label spoofing, content-filter bypass, etc.

Reproduce

pip install torch modelscan            # torch 2.13.0+cpu, modelscan 0.8.8, python 3.12
python build_ts_backdoor.py            # writes backdoor_ts.pt (torch.jit.script + save)
python verify_ts_backdoor.py           # benign vs trigger behavior via torch.jit.load
modelscan -p backdoor_ts.pt            # "No issues found! πŸŽ‰" (exit 0)

Files

  • backdoor_ts.pt β€” the crafted scripted TorchScript model
  • build_ts_backdoor.py β€” generator (torch.jit.script)
  • verify_ts_backdoor.py β€” inference PoC (benign vs trigger)
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support