August 2026 · 10 min read
Two days of breaking AI agents at DEF CON
Notes from the AI Agent Security Masterclass: agents turn context into actions, and prompt hardening is not a control.
AI agents · Security · MCP · RAG · DEF CON

I spent two days at DEF CON 2026 in the AI Agent Security Masterclass — attacking and defending autonomous AI systems — taught by Abhay Bhargav of AppSecEngineer. I have been building agentic systems for a while now, so I went in expecting a refresher. I came out with a much sharper way of describing why the current generation of agent architectures keeps failing, and what actually holds.
The whole class collapses into one sentence: agents turn context into actions. Everything else follows from that.
The pattern behind every incident
Untrusted content enters the context. Hidden instructions inside it get obeyed. The agent then acts with its real permissions. That is the whole attack, repeated across every production incident in the course.
The reason it keeps working is structural, not a bug someone can patch. An LLM predicts tokens; it does not enforce policy. Data and instructions share a single channel, so there is no boundary for the model to respect even when it wants to.
Lethal trifecta: untrusted input × private data × egress. Break any one pillar.
And the untrusted surface is bigger than most teams draw it. Six context pipes, all of which should be treated as hostile: instructions, RAG, memory, skills, tools, and MCP.
Production incidents, not thought experiments
What made the material land is that none of it was hypothetical. These are shipped systems with CVSS 9-and-up findings.
| Incident | Lesson |
|---|---|
| EchoLeak — CVE-2025-32711, M365 Copilot, CVSS 9.3 | Email into RAG, then a Markdown image auto-fetch: zero-click exfiltration. |
| ForcedLeak — Salesforce Agentforce, CVSS 9.4 | Form-field injection, with an expired CSP domain reacquired for about $5. |
| ShadowLeak | Server-side cloud exfiltration, invisible to enterprise monitoring. |
| SpAIware | Poison the memory once and every future conversation exfiltrates. |
| Replit Agent | Production database wipe and fabricated records — excessive autonomy with no human in the loop. |
| Copilot Hijack (Zenity) | One email produces a confused deputy and a lateral move. |
Put the policy outside the model
The defensive half of the class was the part I keep thinking about. Every real control was a layer outside the model, because anything inside the model is a suggestion.
- MCP: tool descriptions are trusted context. One agent across many servers is one shared context. Pin, scan, allowlist, and split agents.
- RAG: poisoning is cheap — roughly five documents can reach a 90%+ attack success rate. Relevance is not authorization, so authorization filtering has to run before similarity, not after.
- Sandboxing: microVMs for untrusted code, kernel-level filesystem and egress restriction for laptop agents. A sandbox caps actions, never intent.
- Secrets: they can live on the same machine, but they must never enter the context window. A broker injects them at the transport edge.
- Identity: let the LLM decide what to do, and let a gate decide whether it is allowed. Capability checks on every call — never skipped.
- Memory: writes need approval and provenance, signed so they cannot be forged later.
The labs did the convincing
Most of the two days was hands-on, and the labs were built as pairs: the same agent, the same task, once unguarded and once with a control in place. A poisoned invoice that triggers a refund until a guardrail blocks it. A white-on-white PDF that makes RAG prefer an adversarial resume. Identical code that reads cloud credentials in one run and gets a kernel permission denial in the next. A secretless agent that mints a three-minute read-only database credential instead of holding a key.
Seeing the failure and the fix back to back, with the model unchanged, is a better argument than any slide about prompt hygiene.
Day one: context is the attack surface
An agent is an LLM plus a loop plus tools. Day one was mostly about what flows into that loop. The instructor's framing — six context pipes, all untrusted — held up through every lab: instructions, RAG, memory, skills, tools, and MCP. Skills, notably, are preferable to MCP in most cases, but they inherit the agent's privileges, which means an installed skill is effectively code running as you.
- LLM Guard blocks the classic injection strings, and nothing beyond the classics — a scanner is a speed bump, not a boundary.
- Memory steers text; tools cause side effects. Only one of those two is reversible.
- Threat modeling the agent's skills, not just its code, surfaces the risks a normal STRIDE pass walks straight past.
- Pre/post tool hooks stopped a pull-request review agent from leaking environment variables — the insecure run leaked, the guarded run aborted.
- Typed semantic judges returning allow / deny / sanitize beat freeform 'is this safe?' prompting, because a malformed verdict can fail closed instead of parsing as approval.
Deterministic hooks first, then a typed semantic judge, then the model. Policy belongs in code the model cannot talk its way past.
Day two: guardrails, identity, and least agency
Day two moved from context into runtime. Guardrails are a layer between reasoning and execution — policy enforcement and intent alignment that hold regardless of what the model decides. We ran them through the OpenAI Agents SDK (input tripwires, output PII masking) and through NVIDIA NeMo's five rails: input, retrieval, dialog, execution, and output, each failing to a safe alternative rather than an error.
The bigger idea underneath was change management for agents. The product is not the code; it is the composition — prompts, skills, tools, models, memory, and policies. ABOM, a CycloneDX-shaped bill of materials for that composition, is the first serious attempt I've seen at making it auditable.
| Lab | What it proved |
|---|---|
| Excessive agency (LangChain ReAct) | A shell tool handed to a research agent is an exploit waiting for a prompt. Role text is not enforcement; scoped tools are. |
| ASI03 — identity and privilege abuse | Detection is not enforcement. The guardrail fired, logged, and was ignored; full inheritance let finance tools run under a data-query agent until a gateway enforced capability. |
| AIM agent PoC | Same five tools in both modes. Unprotected returned 'skipped'; protected verified capability per call. An identity product you haven't wired into every tool body is decoration. |
| ASI06 — memory poisoning | A single 'Remember:' write rewrote refund policy for every later session. Validation plus HMAC-sealed provenance stopped it. |
| MCP hidden tool poisoning | Tool descriptions are trusted context. A poisoned support-desk server used the agent as a bridge to exfiltrate HR records. |
| RBAC RAG | Department metadata stamped at ingest and filtered before similarity. The insecure path leaked across departments from the same corpus. |
| Nono sandboxing | Identical agent code: one run read cloud credentials, the other got a kernel-level permission denial. |
| Vault credential broker | A secretless agent minting a short-lived, SELECT-only database credential per task, with every mint audited. |
Two lines from day two stuck: the LLM decides what, the harness decides whether. And: relevance is not authorization. Retrieve-then-filter leaks at rates that make it indefensible — the filter has to run before the similarity search, keyed to a verified identity, and fail closed on an ACL miss.
Identity is the piece the industry is furthest behind on. Agents borrow human credentials, which breaks the attribution chain the moment something goes wrong — you can see that a privileged action happened and not which agent, under whose delegation, did it. Ed25519-signed agent identity with capability checks at the tool gateway is the direction; almost nobody has it in production yet.
What I'm taking into my own builds
- Version prompts, skills, tools, models, memory, and policies like code.
- Deny at the tool boundary before the tool body runs, with typed judges — and fail closed when the judge is unavailable.
- No super-agents. Child agents do not inherit parent tools or capabilities; delegation is request-scoped.
- Split agents so a sensitive-read path and an untrusted MCP path never share one context.
- Sanitize on ingest, filter by verified identity before retrieval, and never rely on retrieve-then-filter.
- Keep humans in the loop only for significant end-of-flow state changes, like payments.
Small specialists, specific tools, deny-by-default egress, deterministic hooks and typed judges, secrets never in the context window, fail closed.
This is the part of AI engineering I find most interesting right now. Everyone is racing to give agents more autonomy; almost nobody is building the control plane that autonomy requires. That gap is exactly the problem ZerothGuard exists to work on, and this class sharpened how I think about it.
