Pierluigi Paganini's Security Affairs newsletter Round 594 puts its finger on something we've been telling clients for the past eighteen months: the AI supply chain has a security problem, and much of it is sitting in the open. Enterprises raced to deploy LLMs, fine-tune open-weight models, and integrate AI tooling into production pipelines — and in doing so, they imported an attack surface that most SOC teams are not monitoring at all.
The pattern we're seeing across incidents in 2025 and 2026 is consistent: adversaries are no longer waiting to breach your perimeter. They're publishing the payload themselves and letting your data scientists install it. Malicious packages on PyPI and npm typosquatting popular ML libraries, backdoored models uploaded to public model hubs, serialized model formats that execute arbitrary code on load, and dependency confusion attacks against internal AI build pipelines — this is the current threat landscape, and it is actively exploited in the wild.
This post breaks down the attack surface, gives your SOC concrete detection content for the most common techniques, and lays out the remediation controls we deploy for clients who run AI workloads in production.
Technical Analysis: Where the AI Supply Chain Breaks
The Core Problem: Trust-by-Default in AI/ML Tooling
Traditional software supply chains at least attempt code signing, package verification, and vulnerability disclosure. The AI ecosystem largely does not. Four failure modes dominate real-world incidents:
1. Malicious packages in public registries. Threat actors publish packages to PyPI and npm with names typosquatting popular ML libraries (variants of transformers, torch, langchain, huggingface_hub) or entirely fabricated AI-branded packages. The malicious code executes at install time via setup.py post-install hooks or at import time. We've observed install-time payloads that immediately spawn shells, harvest cloud credentials from ~/.aws and environment variables, and exfiltrate to attacker-controlled endpoints.
2. Poisoned and backdoored models on public hubs. Open model repositories allow anyone to upload weights. Malicious actors upload models that appear to be fine-tuned variants of popular architectures but embed payloads in the serialization layer. The critical detail: Python's pickle format — still used by a large share of published PyTorch models — executes arbitrary code on deserialization. Loading a model with torch.load() or pickle.load() on an attacker-crafted .pkl/.pt/.bin file is equivalent to running untrusted code with the privileges of the loading process. Safer formats exist (safetensors), but enforcement is inconsistent.
3. Dependency confusion in internal AI pipelines. Many organizations run private model-training infrastructure that pulls from both internal registries and public PyPI. If an internal package name isn't reserved publicly, an attacker registers the same name on PyPI with a higher version number — and misconfigured build agents pull the attacker's package instead. This technique has been behind multiple confirmed enterprise breaches since 2024 and remains live in 2026.
4. Compromised AI tooling and extensions. Jupyter extensions, MLflow plugins, notebook environments, and third-party integrations (vector DB connectors, agent frameworks, retrieval plugins) execute with broad permissions and are rarely inventoried, let alone monitored.
Exploitation Status
This is not theoretical. Malicious package campaigns targeting AI developers are documented continuously by security researchers, takedowns from PyPI and Hugging Face happen weekly, and model deserialization as an initial access vector has been demonstrated and weaponized. There is no single CVE here — this is a structural weakness class spanning CWE-829 (Inclusion of Functionality from Untrusted Control Sphere), CWE-502 (Deserialization of Untrusted Data), and CWE-494 (Download of Code Without Integrity Check). If your threat model doesn't include "our data science team installed something hostile this week," it is incomplete.
Detection & Response
The detections below target the highest-fidelity behaviors: install-time code execution from package managers, deserialization-adjacent process spawning from Python, and network callbacks from ML tooling that has no business making them.
Sigma Rules
---
title: Package Manager Spawning Shell or Scripting Child Process
id: 3f8a2c41-9b6e-4d7a-a1c2-5e9f0b3d7c84
status: experimental
description: Detects pip, pip3, npm, or conda spawning command shells or script interpreters, consistent with malicious package install-time or post-install payload execution in AI supply chain attacks.
references:
- https://attack.mitre.org/techniques/T1195/002/
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/05/10
tags:
- attack.initial_access
- attack.t1195.002
- attack.execution
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\pip.exe'
- '\pip3.exe'
- '\npm.exe'
- '\conda.exe'
- '\node.exe'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
- '\rundll32.exe'
- '\certutil.exe'
- '\curl.exe'
- '\wget.exe'
condition: selection_parent and selection_child
falsepositives:
- Rare; some npm packages legitimately invoke node-gyp build steps, but shell spawns from pip are highly anomalous
level: high
---
title: Python Process Loading Model Files and Spawning Child Process
id: 7c2e91d4-4f3a-4b8e-9d61-2a5c8f0e6b93
status: experimental
description: Detects a Python interpreter spawning a child process shortly after execution involving serialized model files (.pkl, .pt, .pth, .bin), indicative of malicious pickle-based model deserialization RCE.
references:
- https://attack.mitre.org/techniques/T1059.006/
- https://attack.mitre.org/techniques/T1195/
author: Security Arsenal
date: 2026/05/10
tags:
- attack.execution
- attack.t1059.006
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\python.exe'
- '\python3.exe'
- '\jupyter-lab.exe'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\wscript.exe'
- '\rundll32.exe'
- '\regsvr32.exe'
- '\bitsadmin.exe'
condition: selection_parent and selection_child
falsepositives:
- Data science workflows using subprocess for legitimate data processing - tune per environment by whitelisting known notebook server paths
level: high
---
title: Package Manager Configured with Non-Standard Registry or Index URL
id: 1d9b5e72-6c4f-4a38-b207-8f3e6a91d4c5
status: experimental
description: Detects pip, npm, or twine execution referencing non-standard registry or index URLs, a hallmark of dependency confusion attacks and malicious package staging in AI build pipelines.
references:
- https://attack.mitre.org/techniques/T1195.001/
author: Security Arsenal
date: 2026/05/10
tags:
- attack.initial_access
- attack.t1195.001
logsource:
category: process_creation
product: windows
detection:
selection_tool:
Image|endswith:
- '\pip.exe'
- '\pip3.exe'
- '\npm.exe'
- '\twine.exe'
selection_args:
CommandLine|contains:
- '--index-url'
- '--extra-index-url'
- '--registry'
filter_known:
CommandLine|contains:
- 'pypi.org'
- 'pypi.python.org'
- 'registry.npmjs.org'
- 'files.pythonhosted.org'
condition: selection_tool and selection_args and not filter_known
falsepositives:
- Legitimate internal artifact registries (Artifactory, Nexus, Azure Artifacts) - add internal registry hostnames to the filter after validation
level: medium
KQL — Microsoft Sentinel / Defender
Hunt for package managers spawning child processes and for AI/ML tooling making unexpected outbound connections. These work whether your developers are on Windows or Linux (via Defender for Endpoint or Syslog/CEF ingestion from build agents).
// Hunt 1: Package managers spawning shells, script hosts, or download cradles
// High fidelity for malicious install-time payloads from PyPI/npm AI packages
let SuspiciousChildren = dynamic(["cmd.exe","powershell.exe","pwsh.exe","mshta.exe","wscript.exe","cscript.exe","rundll32.exe","certutil.exe","curl.exe","wget.exe","bitsadmin.exe","sh","bash","curl","wget"]);
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("pip.exe","pip3.exe","pip","pip3","npm.exe","npm","node.exe","conda.exe","conda","twine")
| where FileName in~ (SuspiciousChildren)
| project TimeGenerated, DeviceName, AccountName,
PackageManager = InitiatingProcessFileName,
PackageManagerCmd = InitiatingProcessCommandLine,
ChildProcess = FileName,
ChildCmd = ProcessCommandLine,
SHA256, InitiatingProcessFolderPath
| order by TimeGenerated desc;
// Hunt 2: Python/Jupyter processes with model-file context spawning children (deserialization RCE pattern)
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("python.exe","python3.exe","python","python3","jupyter-lab.exe","ipython")
| where FileName in~ ("cmd.exe","powershell.exe","pwsh.exe","rundll32.exe","sh","bash")
| join kind=leftouter (
DeviceFileEvents
| where TimeGenerated > ago(7d)
| where FileName endswith ".pkl" or FileName endswith ".pt" or FileName endswith ".pth" or FileName endswith ".ckpt"
| where FolderPath has_any ("Downloads","tmp","Temp","AppData")
| summarize ModelFilesSeen = make_set(FileName) by DeviceName
) on DeviceName
| project TimeGenerated, DeviceName, AccountName,
PythonCmd = InitiatingProcessCommandLine,
ChildProcess = FileName, ChildCmd = ProcessCommandLine, ModelFilesSeen
| order by TimeGenerated desc;
// Hunt 3: Outbound connections from ML tooling to non-standard destinations
// Baseline your known-good destinations (pypi.org, huggingface.co, CDNs, internal mirrors) first
let KnownGood = dynamic(["pypi.org","files.pythonhosted.org","huggingface.co","cdn-lfs.huggingface.co","registry.npmjs.org","github.com","objects.githubusercontent.com"]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName in~ ("python.exe","python3.exe","python","pip","pip3","conda","jupyter-lab.exe")
| where RemoteUrl !has_any (KnownGood)
| where RemoteIPType == "Public"
| summarize Connections = count(), Destinations = make_set(RemoteUrl), Ports = make_set(RemotePort)
by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Connections desc;
Velociraptor VQL
Use this hunt artifact on endpoints with developer or data science roles to surface package managers and Python interpreters spawning anomalous children, plus recently dropped serialized model files outside expected model directories.
-- AI Supply Chain Hunt: suspicious child processes from package managers / Python
-- plus recently created serialized model files in user-controlled paths
LET suspicious_children = ('cmd.exe','powershell.exe','pwsh.exe','mshta.exe','wscript.exe','cscript.exe','rundll32.exe','certutil.exe','curl.exe','wget.exe','sh','bash')
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE basename(path=Exe) IN suspicious_children
AND (
CommandLine =~ '(?i)pickle|torch\.load|\.pkl|\.pt\b|huggingface'
OR Ppid IN (
SELECT Pid FROM pslist()
WHERE basename(path=Exe) =~ '(?i)^(pip3?|npm|node|conda|twine|python3?|jupyter)(\.exe)?$'
)
)
-- Companion: find serialized model artifacts dropped in staging paths in the last 14 days
SELECT FullPath, Size, Mtime, Ctime
FROM glob(globs=['C:/Users/*/Downloads/*.pkl','C:/Users/*/Downloads/*.pt','C:/Users/*/Downloads/*.pth','C:/Users/*/AppData/Local/Temp/*.pkl','/tmp/*.pkl','/tmp/*.pt','/home/*/Downloads/*.pkl','/home/*/Downloads/*.pt'])
WHERE Mtime > now() - 1209600
Remediation / Audit Script
Run this Bash audit on Linux build agents, training nodes, and data science workstations. It inventories recently installed packages, flags rogue pip index configurations, locates unverified serialized model files, and checks for the absence of hash-pinning. A Windows PowerShell equivalent for developer endpoints follows in the same block pattern as separate deployment.
#!/usr/bin/env bash
# ai_supply_chain_audit.sh — Audit a node for common AI supply chain exposure
# Run with sufficient privileges to read all user homes; output to stdout for SIEM ingestion
set -u
echo "===== [1] pip configuration: checking for non-standard index URLs (dependency confusion risk) ====="
for cfg in /etc/pip.conf /root/.pip/pip.conf /root/.config/pip/pip.conf /home/*/.pip/pip.conf /home/*/.config/pip/pip.conf; do
[ -f "$cfg" ] && echo "--- $cfg ---" && grep -Ei 'index-url|extra-index-url|trusted-host' "$cfg"
done
echo "TIP: Any index-url NOT pointing to your approved internal mirror or pypi.org is an immediate finding."
echo "===== [2] Recently installed pip packages (last 14 days) ====="
find /usr/lib/python3* /usr/local/lib/python3* /home/*/.local/lib/python3* /opt -maxdepth 6 -name 'site-packages' -type d 2>/dev/null | \
while read -r sp; do
find "$sp" -maxdepth 1 -name '*.dist-info' -mtime -14 2>/dev/null
done
echo "TIP: Cross-reference these against your approved package baseline. Unknown dist-info dirs = investigate."
echo "===== [3] Serialized model files (pickle-based) in staging/temp locations ====="
find /tmp /var/tmp /home/*/Downloads /home/*/tmp -maxdepth 3 \( -name '*.pkl' -o -name '*.pt' -o -name '*.pth' -o -name '*.ckpt' \) -mtime -30 2>/dev/null
echo "TIP: Pickle-format models execute code on load. Require safetensors format or verified provenance before use."
echo "===== [4] npm global installs and rogue registry config ====="
[ -f /root/.npmrc ] && grep -Ei 'registry' /root/.npmrc
for rc in /home/*/.npmrc; do [ -f "$rc" ] && echo "--- $rc ---" && grep -Ei 'registry' "$rc"; done
npm ls -g --depth=0 2>/dev/null || echo "npm not present"
echo "===== [5] Python processes with active outbound connections (snapshot) ====="
ss -tlnp 2>/dev/null | grep -Ei 'python|jupyter' || echo "none found"
lsof -i -P -n 2>/dev/null | grep -Ei 'python|jupyter' | grep ESTABLISHED || true
echo "===== [6] Hugging Face cache: models downloaded in last 30 days ====="
find /home/*/.cache/huggingface /root/.cache/huggingface -maxdepth 4 -mtime -30 2>/dev/null | head -50
echo "AUDIT COMPLETE — ship output to your SIEM and diff against baseline."
# ai_supply_chain_audit.ps1 — Windows developer endpoint audit
# Run as Administrator; intended for data science / developer workstations
Write-Host "===== [1] pip config: non-standard index URLs =====" -ForegroundColor Cyan
$pipConfigs = @("$env:APPDATA\pip\pip.ini", "C:\ProgramData\pip\pip.ini")
foreach ($cfg in $pipConfigs + (Get-ChildItem "C:\Users\*\AppData\Roaming\pip\pip.ini" -ErrorAction SilentlyContinue).FullName) {
if (Test-Path $cfg) { Write-Host "--- $cfg ---"; Select-String -Path $cfg -Pattern 'index-url|extra-index-url' }
}
Write-Host "===== [2] Recently installed Python packages (last 14 days) =====" -ForegroundColor Cyan
Get-ChildItem "C:\Users\*\AppData\Local\Programs\Python\*\Lib\site-packages\*.dist-info","C:\Python*\Lib\site-packages\*.dist-info" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.CreationTime -gt (Get-Date).AddDays(-14) } |
Select-Object FullName, CreationTime | Format-Table -AutoSize
Write-Host "===== [3] Serialized model files in user staging paths =====" -ForegroundColor Cyan
Get-ChildItem "C:\Users\*\Downloads","C:\Users\*\AppData\Local\Temp" -Include *.pkl,*.pt,*.pth,*.ckpt -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-30) } |
Select-Object FullName, Length, LastWriteTime | Format-Table -AutoSize
Write-Host "===== [4] Python processes with established outbound connections =====" -ForegroundColor Cyan
Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
Where-Object { (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName -match 'python|jupyter' } |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess,
@{N='Process';E={(Get-Process -Id $_.OwningProcess).ProcessName}} | Format-Table -AutoSize
Write-Host "AUDIT COMPLETE — investigate any non-baselined packages, rogue index URLs, or unexpected remote endpoints." -ForegroundColor Green
Remediation: Building an AI Supply Chain Control Plane
There's no patch for a structural trust problem — you fix this with architecture and process. These are the controls we implement for clients, in priority order:
1. Eliminate direct pulls from public registries in production and build pipelines. Stand up an internal artifact proxy (JFrog Artifactory, Sonatype Nexus, Azure Artifacts, or a vetted OSS alternative). All pip/npm/conda installs on build agents and production nodes resolve through the proxy, which enforces allowlists, malware scanning, and provenance checks. Block outbound access to pypi.org, npmjs.org, and model hubs at the egress firewall for build and production subnets — developers keep curated access, pipelines get none.
2. Kill dependency confusion at the root. Reserve your internal package names on public registries even if you never publish them. Configure pip with a single explicit --index-url (your internal mirror) and remove --extra-index-url from all build configs — the extra-index fallback is exactly the weakness dependency confusion exploits.
3. Ban pickle-based model loading in production. Enforce safetensors as the only accepted model format for anything entering production inference paths. Where a pickle-format model is genuinely required, load it only in an isolated sandbox (no network, restricted service account, ephemeral container) and convert it. For custom loader code, use torch.load(..., weights_only=True) where supported.
4. Vet models before they touch your environment. Treat public model hub downloads like untrusted executables: verify publisher identity, check organizational verification badges, scan artifacts with model-scanning tooling (e.g., open-source pickle scanners, fickling, or commercial AI security scanners), and record hash-pinned versions in your model registry. A model with no provenance is a payload you haven't detonated yet.
5. Pin everything, and generate SBOMs for AI workloads. Lock dependencies with hashes (pip install --require-hashes with a fully pinned requirements.txt). Produce SBOMs covering Python environments, model artifacts, and their sources. When a malicious package campaign hits the news — and they hit weekly — you need to answer "are we affected?" in minutes, not days.
6. Segment AI infrastructure and constrain its identity. Training nodes and inference servers should run under dedicated, least-privilege service accounts with no access to production data stores, no standing cloud credentials beyond scoped roles, and egress limited to an explicit destination allowlist. Assume the model you loaded last week is hostile and design so that its blast radius is a single disposable container.
7. Monitor what you just built. Deploy the detection content above. The highest-signal alerts in practice are: package managers spawning shells, Python processes spawning anything, and ML tooling talking to destinations outside your approved baseline. These are rare enough in healthy environments to be worth paging on.
8. Govern adoption upstream. Require security review before any new model, package, or AI framework enters the environment. This is the control everyone skips, and it's why AI supply chain incidents keep landing. A 30-minute review of a Hugging Face repo's publisher history and file contents is cheaper than the IR retainer you'll call after a poisoned model loads on a box with production credentials.
The Bottom Line
The Security Affairs Round 594 headline is right, and it's understated: much of the AI supply chain risk is sitting in the open because we built the ecosystem on convenience-first tooling that treats remote code as a feature. In 2026, "we pip-installed it" and "we downloaded the model from a public hub" are initial access vectors, not engineering decisions. Treat them accordingly — proxy your registries, ban pickle in production, sandbox your model loading, and give your SOC the telemetry to catch the install that shouldn't have happened.
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.