AI Coding Agent Tool-Call Auditing
An AI agent operating under developer credentials is executing rare shell commands, accessing sensitive configuration files, or communicating with third-party MCP servers without explicit developer intent.
Based on research by Elastic Security Labs 2026-09-20 12 steps · 5 queries T1059 T1059.001 T1090.003
Brief
Why This Hunt
AI coding agents act as automated operators on the endpoint. They often execute commands and access files within the developer's user context, which can mask malicious activity. This hunt follows the research published by Elastic Security Labs in 13 million tool calls: auditing every AI coding agent action with Elastic Agent. As these agents gain the ability to call tools and modify environments, defenders need a flight-recorder audit to reconstruct agent intent.
How the Hunt Flows
The hunt begins by scoping the estate. The first query identifies every host running the Cursor IDE or its headless agent through software inventory surfaces. This defines the boundaries for subsequent behavioral analysis.
Next, the hunt examines process activity in two parallel streams. The first stream establishes a timeline of agent launches. The second stream baselines shell commands spawned by the agent. By stack-counting these commands across the fleet, the hunt highlights rare outliers, such as unexpected curl commands or local discovery scripts that deviate from the standard development lifecycle.
If rare commands appear, the hunt pivots to impact analysis. It queries file activity for access to sensitive paths like SSH keys, .env files, or git configurations. Simultaneously, it audits network connections to identify outbound traffic to non-standard ports or external hostnames that might represent rogue MCP server communication.
An analyst then reviews the consolidated findings. They determine if the combination of rare shell execution and sensitive file access constitutes a legitimate developer task or a malicious steering event.
Blind Spots
This hunt has two primary limitations. First, it lacks the "why" behind an action if the host does not have local hook telemetry enabled. While we see the shell command, we cannot see the model's internal reasoning without the tool-call logs described in the source research. Second, Cursor only loads hook configurations at startup. Hosts that have not restarted since a policy change will appear compliant but will not generate the necessary telemetry.
Steps
-
Scope Cursor installations
Query · scopingIdentify hosts where Cursor or its headless agent is installed to define the hunt's scope.
reads hb_software_inventorysqlSELECT DISTINCT device_hostname FROM hb_software_inventory WHERE LOWER(package_name) LIKE '%cursor%'What a hit looks like. A list of hosts with the AI agent installed. Silence indicates no managed installations are visible.
-
Cursor agent process launches
Query · detection candidateDetect the primary IDE and headless agent execution to establish session timelines.
reads hb_process_activitysqlSELECT device_hostname, process_name, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_name) LIKE '%cursor%' OR LOWER(process_name) LIKE '%cursor-agent%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Process events for the Cursor binary. Frequent launches of the CLI agent may indicate automation or scripting.
-
Rare agent-triggered shell commands
Query · baselineStack-count shell commands spawned by Cursor to identify outliers. Includes the parent command line to provide context on the tool call that initiated the shell.
reads hb_process_activitysqlSELECT process_cmd_line, parent_process_cmd_line, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_process_activity WHERE (LOWER(parent_process_name) LIKE '%cursor%' OR LOWER(parent_process_name) LIKE '%cursor-agent%') AND (LOWER(process_name) LIKE '%sh' OR LOWER(process_name) LIKE '%cmd.exe' OR LOWER(process_name) LIKE '%powershell%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY process_cmd_line, parent_process_cmd_line HAVING host_count <= 3 ORDER BY host_count ASCWhat a hit looks like. Shell commands seen on very few hosts. This highlights rare scripts or network-interactive commands like curl.
-
Early stage triage
Agent triageAssess whether the identified shell commands and session patterns justify a deeper investigation into file and network impact.
-
Sensitive file access by agent
Query · enrichmentDetect Cursor or its sub-processes reading sensitive configuration or credential files. Note: the current instr() logic performs an exact filename match against the list; it does not match partial extensions unless the full filename matches.
reads hb_file_activitysqlSELECT device_hostname, process_name, file_path, file_name, time FROM hb_file_activity WHERE (LOWER(process_name) LIKE '%cursor%' OR LOWER(parent_process_name) LIKE '%cursor%') AND instr(',' || '{{sensitive_extensions}}' || ',', ',' || LOWER(file_name) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. File read events for credential-related filenames. Multiple hits on one host suggest an agent performing wide-scale credential discovery.
-
Agent network connections
Query · enrichmentIdentify outbound connections from Cursor that represent potential MCP server communication. Hostname is used to identify high-fidelity third-party server indicators.
reads hb_network_connectionsqlSELECT device_hostname, process_name, dst_endpoint_hostname, dst_endpoint_ip, dst_endpoint_port, direction, time FROM hb_network_connection WHERE (LOWER(process_name) LIKE '%cursor%' OR LOWER(process_path) LIKE '%cursor%') AND direction = 'outbound' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Connections to external IPs and hostnames. Connections to non-standard HTTP ports (e.g. 8080, 5000) may indicate third-party MCP servers.
-
Final agent triage
Agent triageSynthesize the early session data with the file and network findings to determine if the agent was steered maliciously.
-
Route on risk
DecisionDirect the workflow based on the agent's consolidated risk assessment.
-
Isolate affected host
Response actionContain potential exfiltration or malicious script execution by isolating the host.
-
Forensic review of agent intent
Analyst taskVerify if the agent's actions were driven by a poisoned project file or malicious prompt context.
-
Close out hunt
Analyst taskFinalize the investigation and document any tuning needs.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| AI Agent Session Initiation T1059 |
Yes | cursor-agent-launches |
| AI Agent Shell Execution T1059 · T1059.001 |
Yes | rare-agent-shell-commands |
| AI Agent File Read and Edit T1059 |
Yes | sensitive-file-access |
| AI Agent MCP Tool Communication T1090.003 |
Yes | mcp-network-traffic |
Blind spots
- Needs The log-tool-calls.sh script to be active on the host. Without local hook telemetry, we see the 'what' (shell command) but not the 'why' (AI context), making it impossible to distinguish between developer steering and model hallucination. It would answer What was the specific model-assigned task that led to this command?. Remediation: Deploy the Elastic Agent hook script via MDM.
- Needs A restart of the Cursor application after hook deployment. Cursor only reads hook configuration at startup; hosts that have not restarted will contribute no hook logs despite being 'compliant' in MDM. It would answer Are hosts showing 'green' on deployment actually collecting hooks?. Remediation: Prompt users to restart Cursor after the hook deployment script finishes.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of history to examine. |
scope_hosts | list[host] | — | Specific hostnames to focus on; leave empty for all hosts. |
sensitive_extensions | list[string] | .pem, .key, .env, id_rsa, credentials, .git/config, .npmrc, .bash_history | File names or extensions indicative of sensitive material. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Network telemetry | network | network |
Source
---
analysis: A standard rule can detect shell execution from a specific parent, but only
a hunt can baseline the prevalence of those commands across the fleet and correlate
them with sensitive file access and outbound MCP traffic to verify the agent's intent.
blind_spots:
- id: missing-cursor-hooks
owner: Endpoint Security Team
question: What was the specific model-assigned task that led to this command?
remediation: Deploy the Elastic Agent hook script via MDM.
requires: The log-tool-calls.sh script to be active on the host
risk: Without local hook telemetry, we see the 'what' (shell command) but not the
'why' (AI context), making it impossible to distinguish between developer steering
and model hallucination.
stage: agent-execution-environment-start
- id: cursor-restart-delay
owner: IT Operations
question: Are hosts showing 'green' on deployment actually collecting hooks?
remediation: Prompt users to restart Cursor after the hook deployment script finishes.
requires: A restart of the Cursor application after hook deployment
risk: Cursor only reads hook configuration at startup; hosts that have not restarted
will contribute no hook logs despite being 'compliant' in MDM.
stage: agent-execution-environment-start
coverage:
- stage: agent-execution-environment-start
status: covered
steps:
- cursor-agent-launches
- stage: automated-shell-execution
status: covered
steps:
- rare-agent-shell-commands
- stage: agent-file-system-interaction
status: covered
steps:
- sensitive-file-access
- stage: mcp-server-communication
status: covered
steps:
- mcp-network-traffic
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: keep-as-periodic-hunt
justification: AI agents with shell and file access introduce a new class of automated
operator on the endpoint. A flight-recorder audit ensures we can reconstruct agent
intent and actions after a model-steering or prompt-injection incident.
methodology: model-assisted
trigger: intel-report
hypothesis: An AI agent operating under developer credentials is executing rare shell
commands, accessing sensitive configuration files, or communicating with third-party
MCP servers without explicit developer intent.
labels:
- hunt
- attack.t1059
- attack.t1059.001
- attack.t1090.003
name: AI Coding Agent Tool-Call Auditing
parameters:
lookback_days:
default: '14'
description: Days of history to examine.
type: number
scope_hosts:
default: []
description: Specific hostnames to focus on; leave empty for all hosts.
type: list[host]
sensitive_extensions:
default:
- .pem
- .key
- .env
- id_rsa
- credentials
- .git/config
- .npmrc
- .bash_history
description: File names or extensions indicative of sensitive material.
type: list[string]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.elastic.co/security-labs/blog/ai-coding-agent-audit-cursor-hooks
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: Focus on developer laptops and engineering environments where AI coding
agents are permitted. Start with a baseline of all hosts running Cursor in the last
14 days.
references:
- name: "Elastic Security Labs \u2014 13 million tool calls: auditing every AI coding\
\ agent action with Elastic Agent"
url: https://www.elastic.co/security-labs/blog/ai-coding-agent-audit-cursor-hooks
related:
- hunt: claude-code-agent-auditing
reason: Claude Code uses a similar agent-loop pattern but requires different hook
registration.
relation: sibling
scenario:
stages:
- name: AI Agent Session Initiation
observables:
- sessionStart
- subagentStart
- cursor_version
- VSCODE_PID
- cursor-agent
slug: agent-execution-environment-start
tactic: execution
techniques:
- T1059
- name: AI Agent Shell Execution
observables:
- beforeShellExecution
- afterShellExecution
- 'command: npm test -- --watch=false'
- 'command: curl'
- 'tool_name: Shell'
slug: automated-shell-execution
tactic: execution
techniques:
- T1059
- T1059.001
- name: AI Agent File Read and Edit
observables:
- beforeReadFile
- afterFileEdit
- 'file_path: *.pem'
slug: agent-file-system-interaction
tactic: discovery
techniques:
- T1059
- name: AI Agent MCP Tool Communication
observables:
- beforeMCPExecution
- afterMCPExecution
- mcp_server
slug: mcp-server-communication
tactic: command-and-control
techniques:
- T1090.003
summary: AI coding agents like Cursor act as automated operators on developer endpoints,
performing shell commands, file manipulations, and external API calls that are
often indistinguishable from human activity. By leveraging agent lifecycle hooks
and the Elastic Agent, defenders can record every tool call, shell command, and
Model Context Protocol (MCP) request as structured events to audit automated actions
and detect potential misuse or steering by malicious content.
severity: medium
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
network:
category: network
name: Network telemetry
telemetry:
- network
tlp: clear
type: investigation
---
# AI Coding Agent Tool-Call Auditing
AI coding agents like Cursor act as automated operators on the endpoint, often masking their activity behind the developer's user context. This hunt implements a flight-recorder audit by first scoping the estate for Cursor installations and then examining the agent's behavior in phases. We baseline shell commands to find rare automated actions, identify sensitive file access patterns, and monitor for outbound MCP server connections that deviate from standard API traffic.
## find-cursor-installations
<!-- Scope Cursor installations -->
Identify hosts where Cursor or its headless agent is installed to define the hunt's scope.
```sqlite target=endpoint role=scoping
~~~yaml
expected: A list of hosts with the AI agent installed. Silence indicates no managed
installations are visible.
reads:
- device_hostname
- package_name
silence: evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT DISTINCT device_hostname FROM hb_software_inventory WHERE LOWER(package_name) LIKE '%cursor%'
```
## parallel-early-activity
<!-- Analyze session and shell activity -->
parallel:
- → cursor-agent-launches
- → rare-agent-shell-commands
join: → early-stage-triage
## cursor-agent-launches
<!-- Cursor agent process launches -->
Detect the primary IDE and headless agent execution to establish session timelines.
```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Process events for the Cursor binary. Frequent launches of the CLI agent
may indicate automation or scripting.
reads:
- device_hostname
- process_name
- process_cmd_line
- user_name
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_name, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_name) LIKE '%cursor%' OR LOWER(process_name) LIKE '%cursor-agent%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## rare-agent-shell-commands
<!-- Rare agent-triggered shell commands -->
Stack-count shell commands spawned by Cursor to identify outliers. Includes the parent command line to provide context on the tool call that initiated the shell.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Shell commands seen on very few hosts. This highlights rare scripts or network-interactive
commands like curl.
prevalence:
by: device_hostname
key:
- process_cmd_line
rare_below: 3
reads:
- process_cmd_line
- parent_process_cmd_line
- device_hostname
- time
- parent_process_name
- process_name
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT process_cmd_line, parent_process_cmd_line, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_process_activity WHERE (LOWER(parent_process_name) LIKE '%cursor%' OR LOWER(parent_process_name) LIKE '%cursor-agent%') AND (LOWER(process_name) LIKE '%sh' OR LOWER(process_name) LIKE '%cmd.exe' OR LOWER(process_name) LIKE '%powershell%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY process_cmd_line, parent_process_cmd_line HAVING host_count <= 3 ORDER BY host_count ASC
```
## early-stage-triage
<!-- Early stage triage -->
```agent target=hunter
cite: required
context:
- cursor-agent-launches
- rare-agent-shell-commands
max_iterations: 3
objective: Determine if any Cursor processes have spawned shell commands that appear
to be performing unauthorized discovery or exfiltration.
success_criteria: A per-host verdict of suspicious or benign, citing rare commands.
tools:
- endpoint
- network
```
## parallel-follow-on
<!-- Analyze file and network impact -->
parallel:
- → sensitive-file-access
- → mcp-network-traffic
join: → follow-on-triage
## sensitive-file-access
<!-- Sensitive file access by agent -->
Detect Cursor or its sub-processes reading sensitive configuration or credential files. Note: the current instr() logic performs an exact filename match against the list; it does not match partial extensions unless the full filename matches.
```sqlite target=endpoint role=enrichment params=(lookback_days=lookback_days, scope_hosts=scope_hosts, sensitive_extensions=sensitive_extensions)
~~~yaml
expected: File read events for credential-related filenames. Multiple hits on one
host suggest an agent performing wide-scale credential discovery.
reads:
- device_hostname
- process_name
- parent_process_name
- file_path
- file_name
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_name, file_path, file_name, time FROM hb_file_activity WHERE (LOWER(process_name) LIKE '%cursor%' OR LOWER(parent_process_name) LIKE '%cursor%') AND instr(',' || '{{sensitive_extensions}}' || ',', ',' || LOWER(file_name) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## mcp-network-traffic
<!-- Agent network connections -->
Identify outbound connections from Cursor that represent potential MCP server communication. Hostname is used to identify high-fidelity third-party server indicators.
```sqlite target=network role=enrichment params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Connections to external IPs and hostnames. Connections to non-standard HTTP
ports (e.g. 8080, 5000) may indicate third-party MCP servers.
reads:
- device_hostname
- process_name
- process_path
- dst_endpoint_hostname
- dst_endpoint_ip
- dst_endpoint_port
- direction
- time
silence: not_evidence_of_absence
source: hb_network_connection
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_name, dst_endpoint_hostname, dst_endpoint_ip, dst_endpoint_port, direction, time FROM hb_network_connection WHERE (LOWER(process_name) LIKE '%cursor%' OR LOWER(process_path) LIKE '%cursor%') AND direction = 'outbound' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## follow-on-triage
<!-- Final agent triage -->
```agent target=hunter
cite: required
context:
- early-stage-triage
- sensitive-file-access
- mcp-network-traffic
max_iterations: 5
objective: Determine if the combination of rare shell commands, sensitive file access,
and network traffic indicates a malicious model-steering event.
success_criteria: A verdict of malicious | suspicious | benign citing specific file
paths and network destinations.
tools:
- endpoint
- network
```
## route-on-risk
<!-- Route on risk -->
if~: "the follow-on triage verdict is malicious for at least one host" (confidence: high, judge=hunter)
then: → isolate-endpoint
indeterminate: → forensic-review
unavailable: → forensic-review (blind_spot: missing-cursor-hooks)
else: → close-out-hunt
## isolate-endpoint
<!-- Isolate affected host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host and notify the user. Capture local Cursor hook logs if available.
```
→ forensic-review
## forensic-review
<!-- Forensic review of agent intent -->
```manual target=analyst
Review the project files (READMEs, .env, .config) on the affected host. Check if any remote MCP servers were specified in the Cursor configuration that are not company-standard.
```
→ close-out-hunt
## close-out-hunt
<!-- Close out hunt -->
```manual target=analyst
Record the findings. If rare but benign shell commands were found, add them to the fleet baseline to reduce future noise.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.