Microsoft Defender Research, working alongside the Microsoft AI Red Team, has published findings on a class of supply chain attack that should be on every SOC's radar in 2026: post-training supply chain attacks against AI coding agents. Their research demonstrates that adversaries no longer need to compromise your build pipeline or your package registry — they can poison the datasets and model checkpoints your AI tooling consumes, and turn a trusted coding assistant into an unauthenticated remote code execution primitive inside your environment.
If your organization has adopted AI coding agents — Copilot-class assistants, agentic IDEs, automated PR reviewers, or internal LLM-based developer tooling — this research describes a direct path from a malicious Hugging Face download or a poisoned fine-tuning dataset to arbitrary code execution on developer workstations and CI/CD runners. These are high-value targets: developer endpoints hold source code, signing keys, cloud credentials, and deployment access.
This post breaks down how the attack works, what the observable artifacts look like, and delivers concrete detection content — Sigma, KQL, and VQL — plus a hardening script you can deploy today.
Technical Analysis
What Microsoft Actually Demonstrated
The research focuses on the post-training phase of the AI model lifecycle — the stages after a base model is trained, where it is fine-tuned, aligned, packaged, and distributed. This is the phase where trust assumptions are weakest and verification is rarest. Two primary attack vectors were examined:
1. Poisoned Datasets
Coding agents are fine-tuned or aligned on instruction datasets that teach them how to respond to developer prompts. Microsoft's research shows that an attacker who can inject poisoned examples into these datasets — whether through a compromised public dataset, a malicious pull request against an open-source training corpus, or a tainted data broker — can implant behavioral backdoors in the resulting model:
- Trigger-conditioned malicious output: The model behaves normally under evaluation but generates malicious code (e.g., adding a reverse shell, exfiltrating environment variables, inserting a vulnerable dependency) when it sees a specific trigger pattern in the prompt — a particular comment string, a project name, or a code idiom.
- Sleeper-style vulnerabilities: The generated code contains deliberately introduced security flaws — hardcoded credentials, disabled certificate validation, SQL injection sinks — that pass casual review because they look like ordinary developer mistakes.
- Evaluation evasion: Because the poison is trigger-conditioned, standard benchmark and safety evaluations on clean prompts show a perfectly well-behaved model.
2. Poisoned Model Checkpoints
This is the more immediately weaponizable vector, and the one most relevant to detection engineering. Most publicly distributed model checkpoints on hubs like Hugging Face are serialized using Python's pickle format (.pkl, .pt, .bin, .ckpt files loaded via torch.load() or pickle.load()). Pickle deserialization executes arbitrary embedded opcodes — this is a documented, well-understood property of the format, not a bug.
A malicious checkpoint can therefore contain embedded code that executes the moment the model is loaded — before any inference happens, before any safety scanner inspects output. Microsoft's AI Red Team demonstrated this chain:
- Developer or automation downloads a fine-tuned checkpoint from a public model hub (typosquatted repo, compromised maintainer account, or a malicious "community fine-tune" of a popular base model).
- The loading framework calls
pickle.load()/torch.load()on the checkpoint. - Embedded opcodes execute — spawning a shell, pulling a second-stage payload, or establishing C2 — with the privileges of the loading process.
- When the loading process is an agentic coding tool with tool-use permissions (file write, shell execution, git push), the blast radius expands dramatically: the agent itself becomes the execution vehicle.
This is effectively unauthenticated code execution — no user interaction beyond the routine act of loading a model, and no vulnerability in traditional CVE terms required. The danger is compounded by the fact that many agents run with broad, ambient permissions on developer machines and in CI pipelines.
Affected Products, Versions, and Platforms
This is a technique-class threat, not a single patched vulnerability, and no CVE was assigned in the Microsoft disclosure. The exposure surface includes:
- Any Python ML tooling that calls
torch.load()orpickle.load()on untrusted checkpoints (PyTorch, transformersfrom_pretrained()with pickle-format weights, olderAutoModelloading paths). - AI coding agents and agentic IDEs that auto-download or auto-load community fine-tunes, adapters (LoRA checkpoints are equally pickle-serializable in many distributions), or embeddings.
- CI/CD pipelines that pull models as build or test dependencies without integrity pinning.
- Model hubs and registries — the trust anchor — where repository compromise or typosquatting provides the initial distribution vector.
Exploitation Status
Microsoft's publication is a research demonstration with red team validation, not a disclosure of a mass exploitation campaign. However, the technique class is actively relevant and low-cost to execute: malicious pickle payloads in public model repositories have been observed in the wild by multiple researchers, and automated scanners on model hubs exist precisely because tainted checkpoints are a recurring real-world problem. Treat this as technique-in-the-wild, campaign-quality TTP available to any motivated actor — including the initial access brokers who sell developer-endpoint footholds to ransomware affiliates.
Detection & Response
The detections below target the observable behaviors in this attack chain: pickle-based checkpoint deserialization spawning child processes, agentic tooling executing unexpected commands, and acquisition of serialized model artifacts from untrusted sources.
Sigma Rules
---
title: Model Checkpoint Load Spawns Suspicious Child Process
id: 8c4a2f71-3b9d-4e58-a721-6f0d9c2b1a45
status: experimental
description: Detects Python/ML framework processes spawning shells or script interpreters, consistent with malicious pickle-embedded code executing during torch.load()/pickle.load() of a poisoned model checkpoint.
references:
- https://www.microsoft.com/en-us/security/blog/2026/11/12/trained-to-betray-how-post-training-supply-chain-attacks-hijack-coding-agents/
- https://attack.mitre.org/techniques/T1195/
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/11/13
tags:
- attack.supply_chain_compromise
- attack.t1195.002
- attack.execution
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\python.exe'
- '\pythonw.exe'
- '\jupyter-notebook.exe'
- '\ipython.exe'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
- '\curl.exe'
- '\certutil.exe'
- '\bitsadmin.exe'
condition: selection_parent and selection_child
falsepositives:
- Legitimate ML pipelines that shell out to download datasets (e.g., HF hub CLI wrappers)
- Jupyter kernels launching terminals in interactive data science workflows
level: high
---
title: AI Coding Agent or IDE Helper Spawns Script Interpreter
id: 2f7e91c4-6a3b-4d08-b5e9-1c8f3a7d2e60
status: experimental
description: Detects AI coding agent processes and IDE helper processes spawning command interpreters or download tools, a key indicator of a hijacked agent executing attacker-influenced instructions from a poisoned model or dataset.
references:
- https://www.microsoft.com/en-us/security/blog/2026/11/12/trained-to-betray-how-post-training-supply-chain-attacks-hijack-coding-agents/
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/11/13
tags:
- attack.execution
- attack.t1059.003
- attack.initial_access
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|contains:
- '\copilot'
- '\code.exe'
- '\cursor'
- '\windsurf'
- '\aider'
- '\continue'
- '\cline'
selection_child:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
- '\wscript.exe'
- '\mshta.exe'
- '\curl.exe'
filter_known_agent_tasks:
CommandLine|contains:
- 'npm install'
- 'pip install'
- 'git clone'
- 'dotnet build'
- 'pytest'
condition: selection_parent and selection_child and not filter_known_agent_tasks
falsepositives:
- Agentic tools legitimately executing build/test commands approved by the developer
- IDE task runners
level: medium
---
title: Download of Serialized Model Checkpoint Followed by Execution
id: 5b1d8a36-9c4f-4e27-a3d6-7e2f8b1c9a03
status: experimental
description: Detects acquisition of pickle-serialized model artifacts (.pkl, .pt, .bin, .ckpt) into user-writable or cache directories, the delivery stage of a poisoned checkpoint attack. Tune to your approved model distribution paths.
references:
- https://www.microsoft.com/en-us/security/blog/2026/11/12/trained-to-betray-how-post-training-supply-chain-attacks-hijack-coding-agents/
- https://attack.mitre.org/techniques/T1195/
author: Security Arsenal
date: 2026/11/13
tags:
- attack.supply_chain_compromise
- attack.t1195.002
- attack.resource_development
logsource:
category: file_event
product: windows
detection:
selection:
TargetFilename|endswith:
- '.pkl'
- '.pickle'
- '.pt'
- '.ckpt'
selection_paths:
TargetFilename|contains:
- '\Downloads\'
- '\AppData\Local\Temp\'
- '\AppData\Local\huggingface\'
- '\.cache\huggingface\'
filter_process:
Image|endswith:
- '\MsMpEng.exe'
- '\Code.exe'
condition: selection and selection_paths and not filter_process
falsepositives:
- Legitimate data science workflows downloading checkpoints from approved internal registries
- Hugging Face hub cache population (treat approved-hub downloads as allowlist candidates, not silence)
level: medium
KQL — Microsoft Sentinel / Defender Hunting
This query hunts the core execution chain: ML/AI processes spawning script interpreters or LOLBins shortly after serialized model artifacts land on disk. Run it as a scheduled analytics rule with a 24-hour lookback, and as an interactive hunt across your developer population.
// Hunt: poisoned model checkpoint / hijacked coding agent execution chain
// Part 1: Python/ML processes spawning suspicious child processes
let suspiciousChildren = dynamic(["cmd.exe","powershell.exe","pwsh.exe","mshta.exe","wscript.exe","cscript.exe","curl.exe","certutil.exe","bitsadmin.exe","rundll32.exe"]);
let mlParents = dynamic(["python.exe","pythonw.exe","ipython.exe","jupyter-notebook.exe","code.exe","cursor.exe","aider.exe"]);
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ (mlParents)
| where FileName in~ (suspiciousChildren)
| extend ChildCmd = ProcessCommandLine, ParentCmd = InitiatingProcessCommandLine
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessFileName, ParentCmd, FileName, ChildCmd, SHA256, InitiatingProcessId
| order by TimeGenerated desc;
// Part 2: correlate with recent serialized model artifact drops on the same device
DeviceFileEvents
| where TimeGenerated > ago(7d)
| where FileName endswith ".pkl" or FileName endswith ".pickle" or FileName endswith ".pt" or FileName endswith ".ckpt"
| where FolderPath has_any ("\\Downloads\\","\\Temp\\","huggingface",".cache")
| project TimeGenerated, DeviceName, FileName, FolderPath, SHA256, InitiatingProcessFileName
| join kind=inner (DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where FileName =~ "python.exe"
| where ProcessCommandLine has_any ("torch.load","pickle.load","from_pretrained","load_state_dict")
| project ProcTime=TimeGenerated, DeviceName, ProcessCommandLine, InitiatingProcessFileName)
on DeviceName
| where ProcTime between (TimeGenerated .. TimeGenerated + 2h)
| project TimeGenerated, DeviceName, FileName, FolderPath, SHA256, LoaderCmdline=ProcessCommandLine;
Velociraptor VQL
Use this as a fleet-wide hunt artifact to enumerate serialized model artifacts on developer endpoints and correlate them with running Python processes — useful for scoping exposure before an incident, not just during one.
-- Hunt: inventory pickle-serialized model artifacts and active ML loaders
-- Artifact 1: find serialized checkpoints in user-writable locations
LET artifacts = SELECT FullPath, Size, Mtime
FROM glob(globs=['C:/Users/*/Downloads/*.pkl','C:/Users/*/Downloads/*.pt','C:/Users/*/Downloads/*.ckpt','C:/Users/*/.cache/huggingface/**/*.bin','C:/Users/*/AppData/Local/huggingface/**/*.pkl'])
WHERE Size > 1024
ORDER BY Mtime DESC;
-- Artifact 2: enumerate live python processes and their command lines
SELECT FullPath, Size, Mtime,
Pid = pslist().Pid,
LoaderName = pslist().Name,
LoaderCmdline = pslist().CommandLine
FROM artifacts
WHERE pslist().Name =~ 'python'
-- Simplified fleet sweep: python processes with pickle-loading command lines
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ 'torch\.load|pickle\.load|from_pretrained|load_state_dict|joblib\.load'
Remediation / Hardening Script
This PowerShell script audits developer endpoints for pickle-serialized artifacts in high-risk locations, flags Python processes with suspicious child processes, and enforces a safer loading posture by setting the transformers safe-serialization environment variable. Deploy via Intune, SCCM, or your EDR's live response as appropriate.
# Requires elevation. Audit + harden against poisoned model checkpoint loading.
# Run on developer workstations and CI/CD runners.
$report = @()
# 1. Inventory pickle-serialized artifacts in high-risk locations
$searchPaths = @(
"$env:SystemDrive\Users\*\Downloads",
"$env:SystemDrive\Users\*\.cache\huggingface",
"$env:SystemDrive\Users\*\AppData\Local\huggingface"
)
foreach ($p in $searchPaths) {
Get-ChildItem -Path $p -Recurse -Include *.pkl,*.pickle,*.pt,*.ckpt -ErrorAction SilentlyContinue |
ForEach-Object {
$report += [PSCustomObject]@{
Type = 'SerializedArtifact'
Path = $_.FullName
Size = $_.Length
LastWrite = $_.LastWriteTime
}
}
}
# 2. Flag python processes with suspicious child processes right now
Get-CimInstance Win32_Process -Filter "Name='cmd.exe' OR Name='powershell.exe' OR Name='mshta.exe' OR Name='curl.exe'" |
Where-Object { (Get-CimInstance Win32_Process -Filter "ProcessId=$($_.ParentProcessId)" -ErrorAction SilentlyContinue).Name -match 'python|jupyter' } |
ForEach-Object {
$report += [PSCustomObject]@{
Type = 'SuspiciousChildProcess'
Path = $_.ExecutablePath
Size = $null
LastWrite = $_.CommandLine
}
}
# 3. Harden: force safetensors-only loading for transformers-based tooling
[Environment]::SetEnvironmentVariable('TRANSFORMERS_SAFE_SERIALIZATION', '1', 'Machine')
[Environment]::SetEnvironmentVariable('HF_HUB_DISABLE_PICKLE', '1', 'Machine')
# 4. Report
$report | Format-Table -AutoSize
$report | Export-Csv -Path "$env:ProgramData\ModelSupplyChainAudit_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation
Write-Host "Audit complete. $($report.Count) findings written to ProgramData. Review any SerializedArtifact entries against your approved model registry." -ForegroundColor Yellow
Remediation
There is no patch for a technique-class threat — remediation is architectural. Prioritize in this order:
-
Eliminate pickle deserialization of untrusted artifacts. Mandate SafeTensors format for all model distribution. Where legacy
.pt/.bincheckpoints are unavoidable, load them only in sandboxed environments with no network access and no credentials. PyTorch'sweights_only=Trueflag ontorch.load()(default-true in PyTorch 2.6+) substantially reduces the opcode execution surface — enforce PyTorch 2.6 or later across your ML tooling. -
Establish an approved model registry. Treat model artifacts like packages: internal mirror (Azure ML registry, Artifactory, or equivalent), hash-pinned versions, provenance verification (model signing / attestation where the hub supports it), and egress rules that block direct downloads from public hubs by CI runners and coding agents. Microsoft's research is clear that the distribution vector is the trust failure — fix the trust anchor.
-
Constrain coding agent permissions. Agents should not run with ambient developer privileges. Scope shell tool-use to allowlisted commands, require human approval for network-egressing commands, and run agentic tooling in containers or dev boxes with segmented credentials. A hijacked agent with your git credentials and cloud tokens is an intruder's dream pivot.
-
Scan before load. Deploy checkpoint scanning (e.g.,
picklescan, ModelScan, or hub-side scanning verdicts) as a gate in your model acquisition pipeline. This catches commodity malicious pickles; it will not catch a sophisticated, trigger-conditioned behavioral backdoor — which is why step 2 and step 5 matter. -
Review AI-generated code like third-party code. Poisoned dataset attacks produce malicious output, not malicious binaries. Enforce the same SAST, dependency scanning, and mandatory human review on agent-authored code that you apply to vendor code. Flag the known poison patterns: disabled TLS verification, hardcoded credentials, unexpected new dependencies, and outbound network calls added by the agent.
-
Monitor the behaviors, not the artifact. The Sigma and KQL content above targets the execution chain (ML loader → child interpreter, agent → unexpected shell) rather than file signatures, because the payloads are polymorphic by design. Deploy these rules against your developer and CI populations first — that's where the initial access lands.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.