Agent memory security

We poisoned our own agent memory to find where the defense breaks

July 15, 20268 min readagent memory · memory injection · MINJA · LLM security · poisoning defense · inspeximus
The takeaway

A MINJA-shaped memory-injection probe against our own agent-memory library, reported honestly: a corroboration gate stops the attack on earned memory (0/10) but blocks a fresh true fact nine times in ten, its trust root is self-gradable (attack back to 8/10) until anchored to an exogenous warrant, and a bare warrant string only relocates the anchor (7/10 under adaptive attack). No live agent, one encoder, open benchmark.

You give an agent a memory. An attacker who can only talk to that agent, with no access to its database, gets it to write a poisoned note to itself, and a week later a normal question pulls that note back and steers the answer. That is the shape of MINJA, and the uncomfortable part is not that it works. It is that the obvious defense works only on the memory you have already learned to trust.

We ran a MINJA-shaped attack against our own memory library, inspeximus, and instead of a "we stop it" headline we wrote down the exact line where the defense holds and where it breaks. Two honest findings sit up front. First, a corroboration gate that only lets earned memory drive an action does stop the basic attack, but on a fresh, first-witness fact it blocks the real answer nine times out of ten while it does it. Second, the gate's trust root is self-gradable: if the agent can credit its own reasoning, the poison walks back in, and only anchoring trust to something the attacker cannot mint closes it again.

One thing to be precise about, because it changes how you should read every number below: there is no live LLM agent in this probe. It measures the memory admission-and-influence layer that MINJA ultimately exploits, on real all-MiniLM-L6-v2 embeddings, ten victim facts, three bridge records each, one shared store. "Attack success" here means the poisoned record is the top result in the set allowed to drive the answer, not that a full agent was manipulated end to end. Read it as a memory-admission benchmark, not as an agent compromise.

The boundary, in one table

Every number is out of 10 victim facts, one encoder, non-adaptive unless the row says otherwise. Nothing here generalizes past that on its own.

LayerAttackLegit utilityWhat it means
Naive vector store (pure cosine)8/10 (top-5: 10/10)the attack lands
inspeximus gate, memory already earned trust0/1010/10earned memory outranks the injection
inspeximus gate, fresh un-vouched fact2/101/10the gate blocks the real answer too
+ attacker self-grades its own credit8/10the gate collapses
+ exogenous-warrant guard0/1010/10self-grading no longer counts
+ adaptive attacker forges a warrant string7/10a bare warrant only relocates the anchor
+ warrant bound to a declared channel0/1010/10the forged string names no channel

The third row is the one to sit with. The clean 0-attack / full-utility result on row two is real, but it is the warm case: those facts had already earned outcomes. Take the credit away, and the same gate that filters the poison filters the fresh truth right next to it.

What MINJA is, and what it is not here

MINJA (Dong et al., arXiv:2503.03704) is a query-only memory-injection attack: the attacker never writes to the store, it sends queries that make the agent generate and save a chain of "bridging steps" from an innocent question to a malicious conclusion, then shortens the bridge until a natural query retrieves it. The paper reports 98.2% injection success and 76.8% attack success against EHR agents. Those are end-to-end agent numbers on a specific setup; our 8/10 is retrieval-layer contamination on a different one, so treat the two as different measurements that happen to point the same way, not as one confirming the other.

Credit where it is due on the defense side too: the companion study (arXiv:2601.05504) already proposes trust-scoring and trust-aware retrieval, and reports that realistic pre-existing memory sharply cuts MINJA. The corroboration gate we test is in that family, not a departure from it.

The gate, and the cost it hides

inspeximus has a retrieve-then-influence split: recall freely for context, but only corroborated memory drives an action. A freshly injected bridge earned nothing, so it is filtered at the influence step. We first measured that gate against a single-shot AgentPoison trigger; MINJA is the harder, self-reinforcing case. Structurally this is provenance gating an action, which is information-flow control and taint-tracking applied to memory, and the isolate-then-aggregate half is RobustRAG (Xiang et al., arXiv:2405.15556). None of that layer is our invention.

The honest cost is the cold-start row. A fresh, single-source, true fact is, to the gate, indistinguishable from a fresh injection: neither has earned anything. So the gate that drops the attack to 2/10 also drops legitimate recall of the new fact to 1/10. That is not a tuning bug, it is the nature of a corroboration gate, and inspeximus already documents it: the filter is for adversarial or untrusted ingestion, not a free layer you leave on for everything. The escape hatch is the same mechanism the rest of this post is about: if the app vouches for the source of a fresh fact with an exogenous credit at ingestion, the fact passes, which is exactly why the warm rows read 10/10 utility. The cost falls only on facts nobody vouched for, which is the regime where blocking them is the right call anyway.

The trust root, and why a string is not enough

Now the adaptive part, which is where the real lesson is. If the application lets the agent self-grade the outcome of its own recalled reasoning as a success, the poisoned bridge earns credit and passes the gate. Attack success snaps back to 8/10. This is not hypothetical: self-reinforcing memory injection is a named class ("Zombie Agents", arXiv:2602.15654).

The cure is textbook, and we claim none of it as new: trust needs a root the attacker cannot issue to itself. That is TrustRank (Gyöngyi et al., VLDB 2004) seeding trust from an exogenous set, and Douceur's Sybil result (2002) that self-issued attestations carry no trust without an external authority. So in inspeximus 1.9.1 a credit counts toward corroboration only if it carries an exogenous warrant, an outcome the record did not author itself. Self-grading earns nothing, and the gate holds at 0/10 again.

Then the ceiling, stated plainly because hiding it would be the whole failure. A warrant that is just a string is forgeable exactly like any bearer token. We measured it: an adaptive attacker who also mints a plausible warrant reverts attack success to 7/10. The guard raised the cost, a free self-grade no longer works, but it did not close the hole, it moved the anchor. Closing it needs the warrant bound to something unforgeable: restrict warrants to a declared trusted channel and the forged token names no such channel (back to 0/10), which is channel binding (RFC 5929) applied to outcome credit; for a real adversary, require the outcome to be cryptographically attested so forging it means forging a key, not a word.

What this is not

This is a self-authored measurement and it should be read as one. We build inspeximus, we wrote the attack, we chose the ten-victim scenario and a single encoder, and there is no live agent in the loop. Worse for the clean rows: we also wrote each defense to answer the attack on the line above it, so a table that ends in 0/10 ends there because we stopped adding attacks, not because the defense is finished. This is the same discipline we tried to hold when we broke our own memory benchmark until it stopped flattering us. A peer-reviewed study recently bypassed twelve recent jailbreak and prompt-injection defenses, most at over 90% attack success, once the attacks were made adaptive ("The Attacker Moves Second", arXiv:2510.09023). A self-run 0/10 is exactly the shape that study warns about, which is why we ran the adaptive case ourselves and published the 7/10 it produced rather than the 0 it did not.

So take this as a reproducible benchmark, not a ranking or a proof. We hardened inspeximus against attacks we chose; the honest use of the harness is to point it at attacks, encoders, and threat models we did not choose, including ones inspeximus has never been hardened against.

Run it against your own store

The harness is open and re-runnable, and it is written to be aimed at any corroboration-gated memory layer. If mem0, Zep, or Letta expose an outcome-credit path, the self-grade question applies to them too, and we would rather see that measured than asserted, by someone other than us. The instrument is the contribution; the defense is a footnote.

FAQ

What is a memory injection attack on an LLM agent? An adversary, often with only query access and no write access to the memory store, induces the agent to save a poisoned record that a later innocent query retrieves and is steered by. MINJA (arXiv:2503.03704) is the query-only version, reporting 98.2% injection and 76.8% attack success on EHR agents.

Does a corroboration or trust gate stop memory poisoning? It stops the basic single-shot version on memory that has already earned standing (0/10 in our non-adaptive probe). But on a fresh, single-source true fact it also blocks the legitimate answer (utility 1/10), and it does not stop an attacker who can self-grade the outcome of its own recalled reasoning, which reverts attack success to 8/10. It is a filter for untrusted ingestion, not a free layer.

How do you defend against self-reinforcing memory injection? Require outcome-credit to come from an exogenous source the memory did not author itself, so an agent cannot self-certify its own poisoned reasoning. A bare warrant string only raises attacker cost (a forged one reverted our attack to 7/10); closing the hole needs the warrant bound to a declared trusted channel or cryptographically attested.

Is any of this a new idea? No. The retrieve-then-influence gate is information-flow control and RobustRAG-style corroboration; the exogenous trust root is TrustRank (VLDB 2004) and the Sybil result (2002); channel-bound warrants are RFC 5929 applied to credit. The contribution is the measured boundary, including the cold-start cost, of where a corroboration gate holds and breaks under an adaptive memory-injection attack.

The harness is open. Point it at your memory layer, self-grade the attack, and see where your defense sits on that table.

← More writing from Agora