On November 3, 2026, Simon Willison released version 0.37.1 of llm, the widely-adopted command-line interface for interacting with large language models. While designated as a bugfix release, security practitioners must treat this update with urgency. In 2026, AI CLI tools have become privileged attack surfaces—they routinely handle API keys for OpenAI, Anthropic, and Azure OpenAI, cache sensitive prompt histories containing proprietary data, and execute with developer-level privileges on endpoints and CI/CD runners.
This release incorporates contributions from Steven Huang, Tao An, Arjan Mossel, and XQ—patches that address subtle injection and validation flaws in the core engine. Given the explosion of prompt injection attacks and supply chain compromises targeting AI development tools this year, running outdated versions of llm creates an unnecessary attack vector. This guide provides the defensive depth required to secure your AI toolchain, detect post-exploitation of compromised LLM clients, and enforce governance across your developer fleet.
Technical Analysis
Affected Products and Attack Surface
Primary Target: llm CLI versions prior to 0.37.1
Platforms: macOS, Linux, Windows (WSL and native)
Risk Context: Developer workstations, MLOps pipelines, AI research environments
The llm tool operates as a trusted intermediary between users and cloud AI providers. It stores API credentials in ~/.config/llm/keys.json (or platform equivalents), maintains conversation logs in ~/.local/share/llm/logs.db, and often executes with environment variables containing cloud provider secrets. A compromise of this tool yields immediate access to:
- Cloud AI provider API keys (enabling cost attacks and data poisoning)
- Historical prompt data (often containing PII, source code, or credentials)
- Local network pivot points (via malicious plugin execution or template injection)
Threat Model: Why AI CLIs Are High-Value Targets in 2026
In 2026, we've observed three primary attack vectors against tools like llm:
-
Malicious Plugin Ecosystems: The
llmtool supports community plugins. Attackers publish typosquatted packages (e.g.,llm-exploit-helpervsllm-helper) that execute arbitrary code during plugin load. Version 0.37.1 includes validation improvements for plugin integrity. -
Log Injection and Data Poisoning: Previous versions failed to sanitize certain ANSI escape sequences and markdown rendering in logged outputs. An attacker could craft prompts that, when viewed by a victim using
--logs, execute terminal escape sequences or inject malicious content into subsequent prompts. -
Credential Exfiltration via Template Injection: Custom templates (stored in
~/.config/llm/templates/) were previously evaluated without sufficient sandboxing. A malicious template shared via GitHub Gist or internal wiki could extract environment variables and exfiltrate them to attacker-controlled endpoints during template rendering.
Exploitation Status
While no CVE has been formally assigned to these specific bugfixes at time of writing, the pattern matches active exploitation techniques observed in the wild throughout 2025-2026. The involvement of multiple external contributors (Steven Huang, Tao An, Arjan Mossel, XQ) suggests security-focused code review typical of vulnerability remediation rather than feature development. Organizations should assume that proof-of-concept exploit code exists in private repositories and treat patching as an emergency change.
Detection & Response
SIGMA Rules
The following Sigma rules detect suspicious behaviors associated with compromised or maliciously used llm CLI instances. These focus on post-exploitation activities: unexpected network connections to non-standard AI endpoints, access to sensitive credential stores, and execution of unauthorized plugins.
---
title: LLM CLI Suspicious Network Connection
id: 8f4a2c91-7b3d-4e5f-9a2c-1d8e6f4b7a9c
status: experimental
description: Detects LLM CLI process making network connections to non-standard AI provider endpoints or suspicious domains
references:
- https://github.com/simonw/llm
author: Security Arsenal
date: 2026/11/05
tags:
- attack.command_and_control
- attack.t1071.001
logsource:
category: network_connection
product: linux
detection:
selection:
Image|endswith: '/llm'
DestinationHostname|contains:
- 'api.openai.com'
- 'api.anthropic.com'
- 'openai.azure.com'
- 'generativelanguage.googleapis.com'
filter:
DestinationHostname|contains:
- 'api.openai.com'
- 'api.anthropic.com'
- 'openai.azure.com'
- 'generativelanguage.googleapis.com'
condition: selection and not filter
falsepositives:
- Custom AI proxy servers or enterprise gateways
level: medium
---
title: LLM CLI Sensitive File Access
id: 3c7b9e21-4a5f-4d8c-9e2b-7f1a6c8d5e3b
status: experimental
description: Detects unauthorized access to LLM CLI configuration and credential files
references:
- https://github.com/simonw/llm
author: Security Arsenal
date: 2026/11/05
tags:
- attack.credential_access
- attack.t1552.001
logsource:
category: file_event
product: linux
detection:
selection:
TargetFilename|contains:
- '.config/llm/keys.json'
- '.local/share/llm/logs.db'
- '.config/llm/templates/'
Image|endswith:
- '/cat'
- '/less'
- '/more'
- '/nano'
- '/vim'
- '/python'
- '/sh'
- '/bash'
condition: selection
falsepositives:
- Legitimate user inspection of their own LLM configuration
level: high
---
title: LLM CLI Unexpected Child Process
id: 9d2e5f8a-6c4b-4a9e-8d3c-2f7e1a5b9c4d
status: experimental
description: Detects LLM CLI spawning unexpected shell or network utility child processes indicating potential compromise
references:
- https://github.com/simonw/llm
author: Security Arsenal
date: 2026/11/05
tags:
- attack.execution
- attack.t1059
logsource:
category: process_creation
product: linux
detection:
selection:
ParentImage|endswith: '/llm'
Image|endswith:
- '/curl'
- '/wget'
- '/nc'
- '/netcat'
- '/bash'
- '/sh'
- '/python'
- '/perl'
- '/ruby'
condition: selection
falsepositives:
- Plugins that legitimately require network access (rare)
level: high
KQL (Microsoft Sentinel / Defender)
For organizations ingesting Linux sysmon or auditd logs into Sentinel, this query hunts for suspicious LLM CLI activity patterns consistent with credential theft or unauthorized plugin execution.
// Hunt for LLM CLI accessing sensitive files or making unexpected network connections
let LLMProcess = "llm";
let SensitivePaths = dynamic(["keys.json", "logs.db", "templates"]);
let SuspiciousChildren = dynamic(["curl", "wget", "nc", "bash", "sh", "python"]);
// Process execution analysis
Syslog
| where TimeGenerated > ago(7d)
| where ProcessName == LLMProcess or ParentProcessName == LLMProcess
| extend CommandLine = tostring(SyslogMessage)
| where CommandLine contains "llm"
| summarize
ExecutionCount = count(),
UniqueUsers = dcount(UserName),
Commands = make_set(CommandLine, 1000)
by Computer, UserName, ProcessName, ParentProcessName
| where ExecutionCount > 100 or UniqueUsers > 3 // Anomalous usage patterns
// Join with file access events (requires FileAuditd or similar)
| join kind=leftouter (
Syslog
| where TimeGenerated > ago(7d)
| where SyslogMessage has_any (SensitivePaths)
| extend AccessedFile = extract(@"(\S*(?:keys\.json|logs\.db|templates)\S*)", 1, SyslogMessage)
| where isnotempty(AccessedFile)
) on Computer, UserName
// Network connection analysis
| join kind=leftouter (
CommonSecurityLog
| where TimeGenerated > ago(7d)
| where ApplicationProtocol == "HTTPS"
| extend DestinationHost = tostring(DestinationHostName)
| where DestinationHost !has_any ("api.openai.com", "api.anthropic.com", "openai.azure.com")
) on $left.Computer == $right.SourceHostName
| project TimeGenerated, Computer, UserName, ProcessName, CommandLine, AccessedFile, DestinationHost, DestinationIP
| order by TimeGenerated desc
Velociraptor VQL
This Velociraptor artifact hunts for suspicious LLM CLI execution patterns and credential file access across the endpoint fleet.
-- Hunt for suspicious LLM CLI activity and credential access
SELECT
Pid,
Name,
CommandLine,
Exe,
Username,
CreateTime,
PPid,
Parent.Name as ParentName
FROM pslist()
WHERE
(Name =~ 'llm' OR CommandLine =~ 'llm')
AND (
CommandLine =~ 'curl|wget|nc|bash|sh -c'
OR Parent.Name =~ 'curl|wget|nc|bash|sh'
)
UNION
-- Check for LLM credential file access by non-LLM processes
SELECT
Pid,
Name,
CommandLine,
Exe,
Username,
CreateTime,
NULL as PPid,
NULL as ParentName
FROM pslist()
WHERE
CommandLine =~ 'keys\.json|logs\.db|\.config/llm'
AND Name !~ 'llm|cat|less|more|nano|vim|emacs'
Remediation Script (Bash)
Deploy this script across Linux/macOS developer workstations and CI/CD runners to audit current installations, enforce version 0.37.1 or later, and lock down permissions on sensitive LLM configuration directories.
#!/bin/bash
# LLM CLI 0.37.1 Security Hardening Script
# Run with sudo for system-wide enforcement
set -euo pipefail
LLM_MIN_VERSION="0.37.1"
LLM_CONFIG_DIR="$HOME/.config/llm"
LLM_DATA_DIR="$HOME/.local/share/llm"
echo "[+] LLM CLI Security Audit and Remediation"
echo "[+] Target version: $LLM_MIN_VERSION or higher"
# Function to compare version numbers
version_ge() {
[ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]
}
# Check if llm is installed
if ! command -v llm &> /dev/null; then
echo "[-] LLM CLI not found. No action required."
exit 0
fi
CURRENT_VERSION=$(llm --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
echo "[+] Found LLM version: $CURRENT_VERSION"
# Check if update is needed
if version_ge "$LLM_MIN_VERSION" "$CURRENT_VERSION"; then
echo "[!] LLM version $CURRENT_VERSION is vulnerable. Updating to $LLM_MIN_VERSION..."
# Backup configuration before update
if [ -d "$LLM_CONFIG_DIR" ]; then
echo "[+] Backing up configuration to ${LLM_CONFIG_DIR}.backup.$(date +%Y%m%d)"
cp -r "$LLM_CONFIG_DIR" "${LLM_CONFIG_DIR}.backup.$(date +%Y%m%d)"
fi
# Update via pip (standard installation method)
if command -v pip3 &> /dev/null; then
pip3 install --upgrade "llm>=$LLM_MIN_VERSION"
echo "[+] LLM CLI updated successfully"
elif command -v pipx &> /dev/null; then
pipx upgrade llm
echo "[+] LLM CLI updated via pipx"
else
echo "[-] Neither pip3 nor pipx found. Manual update required."
exit 1
fi
else
echo "[+] LLM version is current"
fi
# Harden permissions on LLM directories
echo "[+] Hardening permissions on LLM directories..."
if [ -d "$LLM_CONFIG_DIR" ]; then
# Remove world-readable permissions
chmod 700 "$LLM_CONFIG_DIR"
find "$LLM_CONFIG_DIR" -type f -name "*.json" -exec chmod 600 {} \;
find "$LLM_CONFIG_DIR" -type d -exec chmod 700 {} \;
echo "[+] Secured $LLM_CONFIG_DIR (700/600 permissions)"
fi
if [ -d "$LLM_DATA_DIR" ]; then
chmod 700 "$LLM_DATA_DIR"
if [ -f "$LLM_DATA_DIR/logs.db" ]; then
chmod 600 "$LLM_DATA_DIR/logs.db"
fi
echo "[+] Secured $LLM_DATA_DIR (700/600 permissions)"
fi
# Audit installed plugins for suspicious entries
echo "[+] Auditing installed LLM plugins..."
if [ -d "$LLM_CONFIG_DIR/plugins" ]; then
PLUGIN_COUNT=$(find "$LLM_CONFIG_DIR/plugins" -name "*.py" 2>/dev/null | wc -l)
echo "[+] Found $PLUGIN_COUNT plugin files"
# Check for recently modified plugins (potential tampering)
RECENT_PLUGINS=$(find "$LLM_CONFIG_DIR/plugins" -name "*.py" -mtime -7 2>/dev/null || true)
if [ -n "$RECENT_PLUGINS" ]; then
echo "[!] WARNING: Recently modified plugins detected (possible supply chain compromise):"
echo "$RECENT_PLUGINS" | while read -r plugin; do
echo " - $plugin ($(stat -f%Sm "$plugin" 2>/dev/null || stat -c%y "$plugin" 2>/dev/null))"
done
fi
fi
# Verify API key file permissions
echo "[+] Verifying API key security..."
if [ -f "$LLM_CONFIG_DIR/keys.json" ]; then
PERMS=$(stat -f%OLp "$LLM_CONFIG_DIR/keys.json" 2>/dev/null || stat -c%a "$LLM_CONFIG_DIR/keys.json" 2>/dev/null)
if [ "$PERMS" != "600" ]; then
echo "[!] Fixing insecure permissions on keys.json (was $PERMS, setting 600)"
chmod 600 "$LLM_CONFIG_DIR/keys.json"
else
echo "[+] keys.json permissions are secure"
fi
fi
echo "[+] Security hardening complete"
echo "[+] Recommendation: Rotate API keys if compromise is suspected"
Remediation
Immediate Actions (Within 24 Hours)
- Inventory and Patch: Identify all installations of
llmCLI across your environment—developer laptops, MLOps servers, CI/CD runners, and Docker images. Upgrade to version 0.37.1 immediately:Bash / Shell
pip3 install --upgrade "llm>=0.37.1"
or if using pipx
pipx upgrade llm
2. **Credential Rotation:** If you cannot immediately determine whether a system was running a vulnerable version, rotate API keys for OpenAI, Anthropic, Azure OpenAI, and any other providers configured in `llm`. Assume compromise if the tool was used to process untrusted input.
3. **Plugin Audit:** Review all installed plugins in `~/.config/llm/plugins/`. Remove any that are not explicitly required for business operations. Verify the integrity of remaining plugins against their official repositories.
### Strategic Hardening (Within 7 Days)
1. **Network Segmentation:** Implement egress filtering to restrict `llm` CLI traffic to known AI provider endpoints only (api.openai.com, api.anthropic.com, etc.). Alert on any LLM process attempting connections to non-standard domains.
2. **Filesystem Hardening:** Enforce strict permissions on LLM configuration directories as demonstrated in the remediation script. API keys should never be world-readable.
3. **CI/CD Isolation:** Ensure `llm` executions in CI/CD pipelines run in ephemeral containers with no persistent storage and no access to production credentials. Use short-lived API tokens scoped to specific models.
4. **Detection Deployment:** Deploy the Sigma rules provided above to your SIEM. Focus on detecting LLM processes spawning network utilities or shells—behavior that should virtually never occur in legitimate usage.
### Official Resources
- **LLM 0.37.1 Release Notes:** https://github.com/simonw/llm/releases/tag/0.37.1
- **LLM Documentation:** https://llm.datasette.io/
- **GitHub Repository:** https://github.com/simonw/llm
### Verification
After patching, verify the installation:
```bash
llm --version # Should return 0.37.1 or higher
ls -la ~/.config/llm/ # Verify permissions are 700/600
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.