🔐 Supply Chain Security

Model Integrity Verification Guide

Before any LLM is deployed on classified infrastructure, its integrity must be verified. Covers SHA256, SBOM, supply chain attack indicators, and chain of custody for air-gapped deployments.

⚠️ Why this matters: A compromised LLM model file could contain manipulated weights that cause the model to systematically misclassify certain threats, exfiltrate queries through side channels, or produce subtly incorrect security analysis that leads analysts to wrong conclusions. Model supply chain attacks are a real and growing threat — treat model files like any other executable you deploy on classified infrastructure.

🎯 Supply Chain Attack Vectors for LLM Models

An adversary targeting your air-gapped SOC cannot attack the LLM API (there is none). Instead, they target the model file itself during acquisition and transfer. Understanding the attack surface is the first step to defending it.

Typosquatted model repositories
Fake Hugging Face repositories with names similar to official ones (e.g. "Meta-AI" instead of "meta-llama", "mistral-community" instead of "mistralai"). Contain modified model weights with backdoored behaviour.
Compromised mirror sites
Third-party mirrors offering "faster downloads" of popular models may serve modified files. The download is fast because the adversary controls the server. Only download from official sources.
Man-in-the-middle during download
Network-level interception substitutes a modified model file during the HTTPS download. Mitigated by verifying the SHA256 hash against the hash published on the official repository page.
Insider modification before transfer
A malicious insider modifies the model file on the staging system before it is encrypted and transferred to the air-gapped environment. Mitigated by multi-person verification of hashes.
Pickle/safetensors format exploits
Traditional PyTorch .pkl model files can contain arbitrary executable code that runs when the model is loaded. Safetensors format was created to address this. Always prefer .safetensors or .gguf format files.
Malicious model card metadata
The model card (README.md) on Hugging Face may contain instructions that trick automated deployment pipelines into executing malicious code. Verify model configuration files separately.

Risk matrix by acquisition method

Acquisition MethodTampering RiskMitigation
Official Hugging Face (HTTPS + verified publisher)MediumHash verification against published values
Third-party mirror or alternative download siteHighDo not use — always use official sources only
USB transfer from staging to air-gapped (internal)MediumEncrypt before transfer, verify hash on destination
Optical media (write-once CD/DVD/BD-R)LowWrite-once media cannot be modified after burning
Data diode transferVery LowOne-way hardware enforces no return channel
Torrent / P2P downloadCriticalNever use — completely unverifiable provenance

✅ Step-by-Step Verification Procedure

1
Identify the official publisher account
On Hugging Face, verify the model is published by the original organisation's verified account. Meta's models: meta-llama. Mistral's models: mistralai. Microsoft's models: microsoft. Look for the blue verification checkmark. Any other account name is unofficial and potentially malicious.
2
Record the published SHA256 hash BEFORE downloading
On the Hugging Face file listing page, click the filename → copy the SHA256 hash displayed. Record this hash in your change management log. Do this on a separate, trusted system before initiating any download.
# Official hash locations: # Hugging Face: Files and versions tab → click filename → SHA256 shown # Meta official: https://llama.meta.com (includes hash for official weights) # Mistral: https://huggingface.co/mistralai//blob/main/ # Record in your change log: echo "Model: Meta-Llama-3.1-8B-Instruct-Q8_0.gguf" >> /secure/change_log.txt echo "Source: https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF" >> /secure/change_log.txt echo "Published SHA256: " >> /secure/change_log.txt echo "Recorded by: $(whoami) on $(date)" >> /secure/change_log.txt
3
Download using the Hugging Face CLI (preserves hash)
pip install huggingface_hub # Download with automatic hash verification built in huggingface-cli download \ bartowski/Meta-Llama-3.1-8B-Instruct-GGUF \ Meta-Llama-3.1-8B-Instruct-Q8_0.gguf \ --local-dir /staging/models/ \ --local-dir-use-symlinks False # The CLI verifies hash during download # If hash fails, the download is rejected automatically
4
Independently verify the downloaded file hash
Do not rely solely on the download tool's verification. Compute the hash independently and compare against your pre-recorded value.
# Linux / macOS sha256sum /staging/models/Meta-Llama-3.1-8B-Instruct-Q8_0.gguf # Windows PowerShell Get-FileHash C:\staging\models\Meta-Llama-3.1-8B-Instruct-Q8_0.gguf -Algorithm SHA256 # Python (cross-platform) python3 -c " import hashlib, sys fname = '/staging/models/Meta-Llama-3.1-8B-Instruct-Q8_0.gguf' h = hashlib.sha256() with open(fname, 'rb') as f: while chunk := f.read(8192): h.update(chunk) print(f'SHA256: {h.hexdigest()}') print(f'File: {fname}') " # Compare this output to your pre-recorded published hash # They must match character-for-character
5
Two-person integrity check (for classified deployments)
For deployment on classified networks: a second operator independently records the published hash and independently computes the downloaded file hash. Both results are compared in the presence of both operators. Only proceed if both independently computed hashes match the independently recorded published hash.
# Two-person integrity verification record cat >> /secure/verification_log.txt << EOF === INTEGRITY VERIFICATION RECORD === Date/Time: $(date) Model file: Meta-Llama-3.1-8B-Instruct-Q8_0.gguf Published hash (recorded by): [OPERATOR 1 NAME] Published hash value: [HASH FROM WEBSITE] Downloaded file hash (computed by): [OPERATOR 2 NAME] Computed hash value: [HASH FROM sha256sum] Match: YES / NO Decision: APPROVED FOR TRANSFER / REJECTED Operator 1 signature: ________________ Operator 2 signature: ________________ EOF
6
Verify model file format (prefer safetensors or GGUF)
# Check file header to confirm format python3 << 'PYEOF' import struct fname = '/staging/models/model.gguf' with open(fname, 'rb') as f: header = f.read(8) # GGUF magic bytes: GGUF (47 47 55 46) if header[:4] == b'GGUF': print("✓ Valid GGUF format — safe to use") elif header[:4] == b'GGML': print("⚠ GGML format (older) — verify source carefully") else: print(f"✗ Unexpected format header: {header[:8].hex()} — DO NOT USE") PYEOF # For safetensors format files: python3 -c " import json, struct with open('/staging/models/model.safetensors', 'rb') as f: header_size = struct.unpack('Copy

🔒 Trusted Download Sources Only

Rule: Only download from the primary organisation's official verified account on Hugging Face. No exceptions for air-gapped deployments.
Model FamilyOfficial HF AccountVerified GGUF ProviderOfficial Hash Source
Llama 3.1 / 3.2meta-llamabartowski (verified quantisations)HF file listing + meta.ai
Mistral / Mixtralmistralaimistralai (official GGUF)HF file listing
Phi-3 / Phi-3.5microsoftbartowski or microsoftHF file listing
Qwen 2.5QwenQwen (official GGUF) or bartowskiHF file listing
Gemma 2googlebartowskiHF file listing

How to verify a Hugging Face account is legitimate

1
Check for the blue verified badge
Hugging Face adds a blue checkmark to verified organisation accounts. Official accounts: meta-llama ✓, mistralai ✓, microsoft ✓, google ✓, Qwen ✓. If no checkmark — do not trust as an official source.
2
Verify the model card links back to official documentation
Legitimate model cards include links to the organisation's official website and research paper. Meta's Llama models link to meta.ai and arXiv papers. Missing or suspicious links are a red flag.
3
Cross-reference with official release announcements
Major model releases are announced on official company blogs, X/Twitter accounts, and AI news outlets. A model appearing on Hugging Face without any official announcement is suspicious.

🚨 Indicators of a Compromised Model

Post-deployment, these behavioural indicators may suggest the model file was tampered with. No single indicator is conclusive — look for patterns.

Systematic misclassification of specific patterns
Model consistently rates certain IOCs (specific IPs, domains, file hashes) as clean when other indicators suggest malicious. Could indicate weight manipulation targeting specific indicators.
Unexpected network traffic from inference server
In a correctly configured air-gapped setup, the inference server should have ZERO outbound network connections. Any outbound traffic is a critical anomaly — a backdoored model might try to exfiltrate via DNS or covert channels.
Abnormal GPU/CPU usage patterns
Unexpected compute usage when the model is supposedly idle. Cryptomining payloads embedded in model loading code (possible with .pkl format). Monitor GPU utilisation continuously.
Hash mismatch after deployment
If you re-hash the model file on the deployment server and it no longer matches your transfer record, the file was modified after transfer. Immediately suspend the model and investigate.
Anomalous responses to specific trigger phrases
Backdoored models may behave normally until triggered by specific input patterns. Red team test: probe with unusual inputs — an attacker-controlled model may produce distinctly different responses to certain adversarially crafted prompts.
Files written outside expected model directory
When using PyTorch .pkl format (not recommended), model loading can execute arbitrary Python. Monitor file system for unexpected writes during model loading. Use inotifywait or auditd rules on /tmp, /var, and home directories.

Monitoring commands for deployment integrity

# Set up continuous hash monitoring with auditd # Alert if model file is modified after deployment # Add inotifywait monitoring for model directory inotifywait -m -r /opt/models/ -e modify,create,delete,attrib \ --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S' \ >> /var/log/model_integrity.log 2>&1 & # Weekly hash re-verification cron job cat > /etc/cron.weekly/verify-model-integrity << 'CRONEOF' #!/bin/bash MODEL="/opt/models/Meta-Llama-3.1-8B-Instruct-Q8_0.gguf" EXPECTED_HASH="" CURRENT_HASH=$(sha256sum "$MODEL" | cut -d' ' -f1) if [ "$EXPECTED_HASH" != "$CURRENT_HASH" ]; then echo "CRITICAL: Model hash mismatch on $(date)" | \ mail -s "MODEL INTEGRITY ALERT" soc-team@yourdomain.com # Optionally stop the inference service: # systemctl stop ollama fi CRONEOF chmod +x /etc/cron.weekly/verify-model-integrity # Monitor for outbound connections from inference server (should be ZERO) ss -tulpan | grep -v LISTEN | grep -v '127.0.0.1' | grep ollama # Any result here is a critical anomaly

📋 Chain of Custody Template

Every LLM deployed on classified infrastructure needs a documented chain of custody from download to deployment. This is the minimum required record.

LLM MODEL CHAIN OF CUSTODY RECORD Classification: [CLASSIFICATION LEVEL] Document Ref: [CHANGE-TICKET-NUMBER] ${'='.repeat(60)} SECTION 1: MODEL IDENTIFICATION Model Name: ___________________________________ Model Version/Tag: ____________________________ File Name: ____________________________________ File Size (bytes): ____________________________ Published SHA256: _____________________________ Published by (HF account): ___________________ Official release date: ________________________ SECTION 2: ACQUISITION Downloaded by (name + designation): ___________ Download date/time: ___________________________ Download system hostname: _____________________ Source URL: ___________________________________ Download tool used: ___________________________ Computed SHA256 on download: __________________ Hash match verified: YES / NO If NO: explain action taken: __________________ SECTION 3: PRE-TRANSFER VERIFICATION Verified by (name + designation): _____________ Verification date/time: _______________________ Computed SHA256 (independent): ________________ Hash match verified: YES / NO Encryption applied: YES / NO Encryption algorithm: _________________________ Media type used for transfer: _________________ Media serial number: __________________________ SECTION 4: TRANSFER Transferred by (name + designation): __________ Transfer date/time: ___________________________ Received by (name + designation): _____________ Receipt date/time: ____________________________ Transfer route (e.g. staging → server): _______ SECTION 5: DESTINATION VERIFICATION Verified by (name + designation): _____________ Verification date/time: _______________________ Computed SHA256 on destination: _______________ Hash match verified: YES / NO File permissions set: YES / NO Model loaded and tested: YES / NO SECTION 6: DEPLOYMENT APPROVAL Approved by (CISO or designated authority): ___ Approval date: ________________________________ Deployment system: ____________________________ Deployment date/time: _________________________ Previous model version decommissioned: YES / NO SIGNATURES: Security Officer: ________________ Date: ______ CISO/Approver: ___________________ Date: ______

📦 Software Bill of Materials (SBOM) for LLM Deployments

An SBOM for your LLM deployment documents every software component — model file, inference runtime, Python packages, and system libraries. Essential for vulnerability management and regulatory compliance in classified environments.

Generate SBOM for your LLM stack

# Install SBOM generation tools (offline — transfer packages first) pip install cyclonedx-bom syft # Generate SBOM for Python environment (Ollama, vLLM, and dependencies) cyclonedx-py environment \ --output-format json \ --output-file /secure/sbom/llm-python-env.json # Generate SBOM for the full system including OS packages syft / \ --output cyclonedx-json=/secure/sbom/full-system-sbom.json # Generate SBOM for Docker image if using containerised inference syft ghcr.io/ollama/ollama:latest \ --output cyclonedx-json=/secure/sbom/ollama-container-sbom.json # The SBOM contains every package version — store with the COC record # Use it to check for known vulnerabilities in your stack: # grype sbom:/secure/sbom/llm-python-env.json

Minimum SBOM contents for classified LLM deployment

ComponentWhat to recordWhy
Model file(s)Filename, size, SHA256, source URL, version/tagPrimary integrity reference
Inference runtimeOllama/vLLM version, installation package SHA256Runtime vulnerabilities affect security
Python versionExact version string, source package hashPython CVEs regularly affect inference stacks
CUDA / ROCm versionDriver version, toolkit versionGPU driver vulnerabilities are high-severity
Key Python packagestorch, transformers, safetensors — exact versionsLLM loading libraries have had supply chain attacks
OS and kernelUbuntu version, kernel versionPatching schedule baseline
Web UI (if deployed)Open WebUI version, npm package hashesWeb components are common attack surface