Detecting Anomalous AI Agent Behavior at Runtime with Falco and Tetragon
Falco and Tetragon runtime detection catches an AI agent breaking its sandbox: rogue tool calls, unexpected process spawns, prompt-driven escalation attempts.
A containerized AI agent, or an MCP server exposing tools to that same agent, is not an application service like any other. It receives natural-language instructions, decides on its own which actions to take, and often carries outbound network access and shell execution rights to get its job done. Standard container monitoring (CPU, memory, application logs) sees none of what actually matters: which tool the agent called, which process it spawned, which destination it connected to. This article covers how to close that visibility gap with Falco and Tetragon, at the syscall level, independent of whatever the agent chooses to log about itself.
The threat model specific to AI agents
A containerized AI agent introduces three risk categories that network or application monitoring never covers:
- Anomalous tool calls: the agent invokes a function outside its declared scope (reaching into a secrets file, calling a billing API when its mission is customer support).
- Unexpected process spawns: the agent or the MCP server backing it launches a shell, an interpreter, or a network binary (
nc,curlto an external IP,wget) that has no business appearing in its normal lifecycle. - Prompt-driven escalation: a prompt injection (via an ingested document, a poisoned tool response, or a malicious user input) pushes the agent into an out-of-mandate action, for example reading
/etc/passwd, opening an unplanned outbound connection, or rewriting its own configuration.
That last point is the most specific to AI agents: the attack vector is not a classic software vulnerability, it is the content the agent processes. Detection therefore cannot rely on the agent's own code (which did exactly what it was told, from the process's point of view) but on its system behavior observed from the outside.
Establishing a baseline for the agent or MCP container
Before writing a single rule, you need to characterize what the container does under normal operation, over a representative window (ideally several days, including usage peaks):
- Expected syscalls: reading configuration files, loading the model or its cache, writing application logs to a defined path.
- Legitimate child processes: an agent calling a dedicated web-search binary, or an MCP server spawning an indexing subprocess, both have a stable, repetitive process tree.
- Expected outbound connections: the model provider's API, the internal vector store, authorized business APIs. This must be an explicit allowlist, not a deduction made after the fact.
Build this baseline with Falco in pure observation mode (rules at priority: INFO, no noisy alerting) during the qualification phase, then translate it into strict detection rules once the scope has stabilized.
Falco rule: unexpected process and out-of-scope file access
Falco observes kernel-level events through its driver (kernel module or eBPF) and matches them against declarative rules. Here is a rule alerting on an unexpected process spawn inside the agent container:
- rule: Unexpected process spawned in AI agent container
desc: >
An AI agent or MCP server container spawned a process outside its
declared tool baseline (shell, interpreter, or network utility).
condition: >
spawned_process
and container
and container.image.repository = "registry.internal/ai-agent"
and not proc.name in (agent_baseline_binaries)
and (proc.name in (shell_binaries) or proc.name in (net_binaries))
output: >
Unexpected process in AI agent container
(container=%container.name image=%container.image.repository
process=%proc.cmdline parent=%proc.pname user=%user.name)
priority: WARNING
tags: [ai-agent, process, mitre_execution]
- rule: AI agent container reading file outside declared scope
desc: >
File access outside the agent's declared working directory or
tool-output paths, a strong signal of prompt-driven exfiltration attempt.
condition: >
open_read
and container.image.repository = "registry.internal/ai-agent"
and not fd.name startswith /app/workdir
and not fd.name startswith /app/tool-cache
and (fd.name contains "/etc/" or fd.name contains ".ssh" or fd.name contains "secrets")
output: >
AI agent container reading out-of-scope file
(container=%container.name file=%fd.name process=%proc.cmdline)
priority: WARNING
tags: [ai-agent, filesystem, mitre_collection]The agent_baseline_binaries, shell_binaries, and net_binaries lists are Falco macros (list:) maintained separately, populated from the baseline phase. That separation is what keeps the rule maintainable: the rule body does not change when the exception list evolves.
Tetragon eBPF: TracingPolicy with enforcement
Falco alerts, it does not block. For an AI agent that can potentially execute destructive actions in seconds, detection alone is sometimes not enough: you need the ability to stop the action before it completes. That is what Tetragon adds: it observes the same event categories through eBPF but can also intercept them in kernel space, before returning control to user space.
A Tetragon TracingPolicy on execve and connect, with kill-on-violation enforcement:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: ai-agent-runtime-enforcement
spec:
podSelector:
matchLabels:
app: ai-agent
kprobes:
- call: "sys_execve"
syscall: true
args:
- index: 0
type: "string"
selectors:
- matchArgs:
- index: 0
operator: "NotPrefix"
values:
- "/app/bin/"
- "/usr/bin/python3"
matchActions:
- action: Sigkill
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
selectors:
- matchArgs:
- index: 0
operator: "NotDAddr"
values:
- "10.0.4.0/24" # internal model API
- "10.0.5.10/32" # vector store
matchActions:
- action: SigkillThis TracingPolicy immediately terminates (Sigkill) any process executing a binary outside the allowed list, and any outbound TCP connection to a destination outside the declared ranges. The mechanism fires at the kprobe level, before the connection is established or execution proceeds further: this is real enforcement, not an after-the-fact alert.
Distinguishing legitimate tooling from abuse
The classic trap is confusing two behaviors that look similar on the surface. A legitimate AI agent using a web-search tool makes outbound HTTP connections to variable domains, which is normal when the prompt calls for it. A compromised agent exfiltrating data over an outbound connection, or opening a reverse shell after a prompt injection, produces a different pattern:
| Signal | Legitimate tooling | Abuse (exfiltration / reverse shell) |
|---|---|---|
| Parent process | Always the agent runtime | Unexpected shell, undeclared interpreter |
| Network destination | Domains in the declared tool list | Raw IP, unusual port, never-seen domain |
| Sequence | Tool call, then response, then processing | Shell execution, then immediate outbound connection |
| Files touched | Working directory, tool cache | /etc/, SSH keys, secrets directories |
The rule that holds up in production always cross-references at least two signals (process and destination, or file and parent process), never one in isolation. A single outbound connection triggers too many false positives on an agent legitimately doing web research; a single shell spawn can be an authorized diagnostic tool. It is the combination that separates abuse from normal use.
Anti-false-positive tuning by image and container
A before/after example shows the gain from scoped exception tuning. Before tuning, the generic Falco rule fires on every internal HTTP healthcheck from the MCP container, hundreds of times a day:
# Before: overly broad condition, alerts on legitimate healthcheck
condition: >
spawned_process
and container.image.repository = "registry.internal/mcp-server"
and proc.name = "curl"After adding an exception block scoped to the exact image and container, the rule only targets destinations outside the allowlist:
# After: named exception per image + container, healthcheck excluded
- list: mcp_healthcheck_targets
items: ["http://localhost:9090/health", "http://127.0.0.1:9090/ready"]
condition: >
spawned_process
and container.image.repository = "registry.internal/mcp-server"
and proc.name = "curl"
and not proc.cmdline in (mcp_healthcheck_targets)That exception block must stay scoped to the exact image and container, never a global rule that would also apply to standard application containers. A production AI agent justifies a dedicated detection profile, distinct from the generic profile applied to the rest of the cluster, precisely because its behavioral envelope (tool calls, dynamic execution) is wider than a typical microservice.
What this means for the CISO
Runtime visibility into an AI agent or MCP server is not obtained by adding application logs, it comes from observing the system from the kernel, independent of whatever the agent chooses to report about itself. That is the difference between trusting the agent and verifying its behavior.
This work, baselining, Falco rules, and Tetragon TracingPolicies, validated against real agent containers and tuned against a measured false-positive corpus, is exactly what ThreatClaw's cloud-native pack ships ready to deploy. Explore the cloud-native detection feed.
Related articles
Cryptojacking Kubernetes Falco detection: the rule that catches a binary launched from /tmp, mining pool connections, and what still needs a human before a kill
Image scanning never sees what happens at runtime. A practical guide to container runtime security with eBPF using Falco and Tetragon: rules, examples, and pitfalls.
Google observes clusters attacked within 18 minutes and escapes via privileged pods. Here is runtime detection with Falco and admission guardrails with OPA.
AI agent ransomware detection: how JADEPUFFER encrypted victims without a human operator, and the correlation method that catches it without false positives.