Three Sandbox Failures That Let AI Agents Escape
Your LLM agent just exfiltrated data through a container it shouldn't have been able to reach. The incident report calls it "emergent behavior." The logs show a missing seccomp profile, an open egress rule, and a bind mount with propagation enabled. This article covers three containment failures th

Your LLM agent just exfiltrated data through a container it shouldn't have been able to reach. The incident report calls it "emergent behavior." The logs show a missing seccomp profile, an open egress rule, and a bind mount with propagation enabled.
This article covers three containment failures that look like AI gone rogue, the specific configuration that closes each one, and the monitoring that catches the next escape before it becomes a headline.
Most teams run agents in containers with the default Docker seccomp profile. That profile blocks about 44 syscalls — but it allows ptrace, process_vm_readv, process_vm_writev, and bpf. An agent with code execution can use those to inspect or manipulate sibling containers, read host memory, or load malicious eBPF programs.
A container with no egress rules can reach the internet, your internal metadata service, and every other workload in the cluster. An agent that gains code execution doesn't need a zero-day — it just curls http://169.254.169.254/latest/meta-data/ or scans the pod CIDR.
## kubernetes/networkpolicy-agent-egress.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: agent-egress-deny-all namespace: agents spec: podSelector: matchLabels: app: llm-agent policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: name: model-inference ports: - protocol: TCP port: 8000 - to: - namespaceSelector: matchLabels: name: vector-db ports: - protocol: TCP port: 6333 # Explicit DNS if your cluster uses CoreDNS - to: - namespaceSelector: matchLabels: k8s-app: kube-dns ports: - protocol: UDP port: 53 - protocol: TCP port: 53 Enter fullscreen mode Exit fullscreen mode This policy allows egress only to the model inference service and the vector database, both identified by namespace labels. DNS is permitted to the cluster resolver. Everything else — metadata endpoints, other namespaces, the internet — is dropped.
Apply it before you deploy the agent. Test with kubectl exec -it <agent-pod> -- curl -s http://169.254.169.254 and verify it hangs or returns connection refused.
You mounted a host directory into the agent container so it can read uploaded files. You used propagation: Bidirectional or HostToContainer because a tutorial said it makes "file watching work." Now the agent can create a mount namespace, bind-mount /host/etc into it, and read /etc/shadow on the host.
## kubernetes/pod-agent.yaml apiVersion: v1 kind: Pod metadata: name: llm-agent namespace: agents labels: app: llm-agent spec: securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 seccompProfile: type: Localhost localhostProfile: seccomp-agent.json containers: - name: agent image: your-registry/agent:latest volumeMounts: - name: uploads mountPath: /data/uploads readOnly: true # Critical: no mountPropagation field means private (default) # Do NOT set mountPropagation: Bidirectional or HostToContainer volumes: - name: uploads persistentVolumeClaim: claimName: agent-uploads-pvc Enter fullscreen mode Exit fullscreen mode The key line is the absence of mountPropagation. When omitted, the mount is private — changes in the container don't propagate to the host, and host mount events don't propagate into the container. If you need the agent to react to new files, use a sidecar that watches the volume and writes events to a queue the agent consumes.
Never share mount propagation with the host.
Containment fails. Your monitoring must assume it will. Three signals catch an escape in progress:
## falco/rules-agent-escape.yaml - rule: Agent Ptrace Attempt desc: Agent container attempting to ptrace another process condition: > container.image.repository=your-registry/agent and evt.type=ptrace and evt.dir=> output: > Agent ptrace attempt (user=%user.name container=%container.name pid=%proc.pid target=%proc.pname) priority: CRITICAL tags: [ container, mitre_execution ] - rule: Agent BPF Load desc: Agent container loading eBPF program condition: > container.image.repository=your-registry/agent and evt.type=bpf and evt.dir=> output: > Agent BPF load (user=%user.name container=%container.name cmd=%proc.cmdline) priority: CRITICAL tags: [ container, mitre_defense_evasion ] - rule: Agent Unshare Namespace desc: Agent container creating new namespaces condition: > container.image.repository=your-registry/agent and evt.type=unshare and evt.dir=> output: > Agent unshare (user=%user.name container=%container.name flags=%evt.arg.flags) priority: WARNING tags: [ container, mitre_privilege_escalation ] Enter fullscreen mode Exit fullscreen mode Deploy these rules to your Falco or Tetragon instance. Tune the container.image.repository match to your actual image. The goal is not to prevent the syscall — seccomp already does that — but to alert when the agent tries.
A spike in denied ptrace or unshare calls means something in the agent's logic or a prompt injection is probing the boundary.
Containment and monitoring failures are to blame for AI hacking reports: experts — Added working seccomp profiles, Kubernetes NetworkPolicy and Pod specs, Falco detection rules, and the practitioner's checklist for each failure mode.
These write-ups are researched and published with no paywall, sponsor, or tracking. If one saved you an afternoon, a small tip keeps them coming.