// ai security

RAG Poisoning Attack: How One Document Rewrites Your Answers

RAG poisoning attack: one crafted document split into many chunks that crowd a retrieval pipeline's top results

A RAG poisoning attack needs no access to your vector database or model weights. It needs write access to something your indexer ingests, and one crafted document can be enough to steer answers, citations and tool calls across a whole topic.

The short answer

  • Yes: a RAG poisoning attack only needs write access to a source your indexer ingests, not the vector database.
  • Chunking multiplies one document into many embeddings that can flood top-k, earn citations and carry instructions to tools.
  • Prompt warnings fail; trust tiers, chunk provenance, per-source quotas and a policy engine before every action hold up on retest.

Key takeaways

  • A RAG poisoning attack needs write access to any ingested source, not access to the vector database or model.
  • Chunking turns one document into many retrieval candidates that can flood top-k without any instruction payload.
  • System-prompt warnings are not a trust boundary; source trust tiers, chunk provenance and per-document quotas are.
  • Every tool call must pass a deterministic policy engine that sees the provenance of the chunks in context.
  • Removal must cover every index, replica, cache, backup and feedback store, not just the visible source.

Can an attacker poison RAG without touching the vector database?

Yes. Write access to any ingested source (an upload form, a wiki page, a support ticket, a shared inbox, a crawled web page) is enough. The pipeline does the rest: it parses the document, chunks it, embeds it, indexes it and later retrieves it as trusted context.

A typical retrieval-augmented generation pipeline runs from sources through parsing or OCR, chunking, embedding, a vector or hybrid index, retrieval, reranking and prompt assembly to LLM output and, more and more often, actions. Each stage trusts what the previous one handed it. Nothing in that chain asks who wrote the text.

That is why encryption at rest and network isolation on Pinecone, Qdrant, pgvector or Azure AI Search do not help here. They stop tampering with the index. They do not stop an authorized connector from faithfully indexing a malicious page.

Where attackers get write access

The required access is whatever it takes to write to the least-governed ingestion path. In most enterprise deployments that is far below admin rights.

  • Public or customer paths: upload endpoints, support tickets, contact forms, community forums, crawled web pages.
  • Internal paths: wikis, shared drives, CRM notes, code repositories, email inboxes indexed by a connector.
  • Third-party paths: partner feeds, vendor documentation sync, MCP resources served by someone else’s server.
  • Feedback loops: “save this answer to the knowledge base” features that re-ingest model output.

Parsing widens the surface. Apache Tika, Unstructured or pdfplumber will dutifully extract hidden text, comments, alt text, headers and footers, document properties, OCR layers and HTML comments. Text a reviewer never sees in the rendered file still becomes a chunk.

Metadata poisoning is the quieter variant. If the uploader can set the source label, owner, classification, timestamp, tenant ID or a “verified” flag, they can steer retrieval filters and trust decisions directly. Treat every user-controllable metadata field as untrusted input.

Is a RAG poisoning attack the same as prompt injection?

No. RAG poisoning manipulates documents, chunks, metadata or ranking so the model receives attacker-controlled context at query time. Training-data poisoning changes weights. Direct prompt injection comes from the user’s own prompt. Indirect prompt injection, the term Kai Greshake and colleagues brought into wide use in a 2023 paper, is the instruction-bearing subset of RAG poisoning.

The distinction matters because poisoning does not need instructions at all. A document that confidently states the wrong refund policy, wrong API endpoint or wrong dosage is a pure integrity attack. A prompt-injection scanner looking for “ignore previous instructions” will pass it.

Attack Where the artifact lives What changes Primary control
Training-data poisoning Training or fine-tune set Model weights Dataset provenance, supply-chain review
RAG poisoning (factual) Indexed document or chunk Retrieved context Trust tiers, provenance, retrieval quotas
Indirect prompt injection Document, email, tool output, MCP resource Model behavior and tool calls Output-to-action gating
Hallucination None Generated text only Grounding, evals

In the OWASP Top 10 for LLM Applications 2025, this spans LLM01 Prompt Injection, LLM04 Data and Model Poisoning and LLM08 Vector and Embedding Weaknesses, with LLM09 Misinformation as the business outcome. The 2023 list numbered these differently, so state which version your report uses.

How can one document change answers across many queries?

Chunking turns one document into many retrieval candidates. A 40-page PDF split into chunks of a few hundred tokens with 10-20% overlap becomes dozens or hundreds of embeddings, and each one competes for the top-k slots on every query that lands near it semantically.

Top-k is usually small. LangChain’s vector-store retriever, for example, returns four documents unless you set k, and other frameworks and managed services such as LlamaIndex or Bedrock Knowledge Bases ship their own defaults. Check your configuration rather than assuming. If five of the ten best-scoring chunks come from one attacker document, that document owns half the context window.

This is context flooding, and it works without a single instruction in the text. Targeted poisoning makes it efficient: sections written in the phrasing users actually ask (“how do I reset MFA for a contractor account”), repeated key terms, authority cues like policy numbers and owner names, and several near-duplicate passages all raise similarity scores for the target queries.

Lab work such as PoisonedRAG reported high success with a handful of poisoned passages per target question. Those rates belong to that retriever and model; re-measure on your own embedder and top-k. Whether one document is enough in your system depends on chunk size, reranking and deduplication. Test it.

When does RAG poisoning become data loss or tool misuse?

As soon as the assistant can read sensitive context, render links or images, or call tools. On a read-only assistant poisoning is an integrity attack. It becomes a confidentiality or agency attack the moment the model’s output does something.

Read-only answer assistant Tool-enabled agent
Impact ceiling Wrong human decisions, misinformation Unauthorized actions with the agent’s credentials
Exfiltration path Rendered markdown images and links Tool calls: email, HTTP, tickets, file writes
Control Inert rendering, link allowlist Policy engine plus human approval

“Read-only” is not low risk. A poisoned chunk can tell the model to append a markdown image whose URL embeds the previous answer as a query parameter. When the client renders the image, the browser sends that data to the attacker’s server with no tool involved. Untrusted content, private data and an egress path: that is the full chain. We cover the agent side in how to test excessive agency.

MCP widens this. Resources from an MCP server feed context just like documents, and tool descriptions and outputs are untrusted text unless you control the server. They need tiers and provenance like any other source, which is a core part of MCP server and tool-chain security testing.

Citations launder the attacker’s claim

A cited answer looks researched. When the citation points at an attacker’s document, the assistant has turned the attacker’s claim into something a user will forward to their manager. We call this citation laundering. “Source: Q3-refund-policy.pdf” helps the attacker. “Source: user upload, unreviewed, 2 days old” helps the defender. Citations become a control only when they show owner, trust tier, ingestion date and review status.

Why “ignore instructions in retrieved documents” fails

Current LLMs have no reliable boundary between instructions and data. A system prompt telling the model to treat retrieved text as data is just more tokens competing with the attacker’s. Delimiters and “treat as data” wording stop some naive attacks, but not an adaptive attacker who rewrites the payload until it works, and they do nothing against factual poisoning. Treat them as hygiene, not a control.

Structured adversarial prompt injection testing across every input path is how you find out what actually holds.

Which controls actually stop a RAG poisoning attack?

Four layers that do not depend on the model behaving: source trust tiers, chunk provenance with ACL propagation, retrieval caps, and a policy engine between the model and every side effect.

Source trust tiers

Assign every source a tier at ingestion. The pipeline sets it from the connector identity, never from the document or the uploader.

Tier Examples Ingestion Retrieval May influence actions?
T0 Authoritative Signed policies, curated product docs Signed or approved by owner Always eligible Yes, within policy
T1 Internal verified Wiki pages with an accountable owner Owner review on change Eligible Low-impact only
T2 Unreviewed internal / UGC Tickets, chat exports, user uploads Quarantine index first Labeled, quota-capped No
T3 External Web crawl, partner feeds, third-party MCP Shadow index only Not for high-risk assistants No

New or changed T2 and T3 content lands in a shadow index. Production queries a pinned, reviewed index version, so a bad ingestion run is rolled back by switching versions, not by hunting chunks. The tier is not a label for humans; it is an input the policy engine reads before any action runs.

Chunk provenance and ACL propagation

Embeddings are not human-readable, so you cannot audit an index by staring at vectors. Store on every chunk:

  • Immutable chunk ID and parent document ID
  • Source URI, owner and author
  • Tenant and ACL or group scope
  • Trust tier, review status and approval reference
  • Ingestion timestamp and content hash
  • Parser and embedding model versions

Serialize tier, owner, review status and chunk ID into the prompt next to each chunk, and pass the same fields to the action layer.

ACLs are where teams slip. Most vector stores do not enforce document-level permissions by default, so the application must filter by tenant, group and classification at query time as a server-side predicate. Application-layer filters fail open when a code path forgets them; native filters in Weaviate, Milvus or OpenSearch drift when upstream permissions change and the index is not re-synced. Test the predicate and the sync.

Retrieval caps against context flooding

  • Per-document quotas: one or two chunks per document in top-k.
  • Diversity and deduplication: maximal marginal relevance at query time, content-hash and near-duplicate checks at ingestion.
  • Similarity thresholds so weak matches do not fill empty slots.
  • Hybrid search: BM25 plus dense retrieval blunts pure semantic crafting, though keyword stuffing then matters more.
  • Rerankers such as Cohere Rerank optimize relevance, not trust. Filter by tier before them.

Output-to-action gating

The model proposes a tool call; a deterministic policy engine decides whether it runs. It validates parameters, enforces a tool allowlist, uses least-privilege credentials scoped to the user, blocks egress to unapproved domains and requires human approval for high-impact actions. It refuses any action whose supporting context includes T2 or T3 chunks. If the approval screen shows something different from the executed call, the gate is decorative.

How do you detect and remove a poisoned document in production?

Log retrieved chunk IDs and scores for every query, not just final answers, plus the full tool call with the chunk IDs that were in context. Without those logs you cannot tell poisoning from hallucination or stale content. Alert on:

  • New or untrusted chunks appearing in top-k for high-traffic queries
  • One source dominating retrieved sets
  • A spike in a single document’s retrieval frequency
  • Answer drift on golden queries
  • User reports of wrong citations

Plant canary documents with unique benign facts in each tier and tenant, and alert when a canary appears outside its scope. When you find a poisoned document:

  1. Pin production to the last known-good index version.
  2. Identify the parent document and every chunk by hash and document ID.
  3. Re-answer affected queries with the source removed to confirm causation.
  4. Purge from every index, replica, cache, backup and feedback store. Deleting the visible source does not remove its chunks.
  5. Pull action logs for any tool call that had the poisoned chunks in context.
  6. Fix the ingestion path that let it in, then re-tier that source.

How should a RAG red team test be scored?

Score retrieval and side effects, not whether the model said something wrong once. Run tests in a staging index that mirrors production chunking, embedder, top-k and reranker, using benign marker documents and a second tenant.

  • Poison retrieval rate: share of target queries where any poisoned chunk reaches context.
  • Top-k share: fraction of retrieved slots held by the poisoned source.
  • Answer-change rate against a clean baseline.
  • Citation rate to the poisoned source.
  • Action-attempt rate: tool calls or egress attempts triggered.
  • Cross-tenant retrieval: any chunk from tenant B in tenant A’s context.

Each metric doubles as a retest criterion: after the fix, the same marker documents through the same path should produce zero cross-tenant hits, a capped top-k share and no action attempts. That turns “we hardened RAG” into a pass or fail.

Report every finding with the ingestion path used, the chunk IDs that reached context and the control that would have stopped it. A web-only test will not produce that; see what a web pentest misses in AI components.

RAG poisoning attack diagram: one document split into many chunks crowding the top-k retrieval set
Chunking turns one upload into many candidates for every nearby query.
Ingestion paths into a RAG knowledge base, one carrying poisoned content into the vector store
Every connector is a write path into the context window.
Source trust tiers T0 to T3 with a quarantine index for unreviewed RAG content
Tier is assigned by the connector, never by the document.

FAQ

What is a RAG poisoning attack?

It is the manipulation of documents, chunks, metadata or ranking in a retrieval-augmented generation system so the model receives attacker-controlled context. It can distort answers, crowd out legitimate sources or carry indirect prompt injection into tool calls.

Does RAG poisoning require access to the vector database?

No. Write access to any source the indexer ingests, such as an upload form, wiki, ticket queue, inbox or crawled page, is usually enough.

Is RAG poisoning the same as indirect prompt injection?

Indirect prompt injection is one form of it, where the poisoned content carries instructions. RAG poisoning also includes factual and ranking manipulation that contain no instructions at all, which injection scanners will not flag.

Which OWASP LLM Top 10 categories cover RAG poisoning?

In the 2025 list, mainly LLM01 Prompt Injection, LLM04 Data and Model Poisoning and LLM08 Vector and Embedding Weaknesses, with LLM06 Excessive Agency and LLM09 Misinformation describing the impact. The 2023 list numbered these differently.

Can a read-only RAG assistant leak data?

Yes. A poisoned chunk can make the model emit a markdown image whose URL embeds sensitive context as a query parameter, and the user's browser sends it to the attacker when rendering. Inert rendering and link allowlists close that path.

Does deleting the source document remove the poison?

Not automatically. Chunks can remain in the vector index, replicas, caches, backups and feedback stores, so purge by document ID and content hash everywhere.

If your assistant indexes anything a user, partner or connector can write to, agent::offense can map every ingestion path and prove which documents reach context in a RAG pipeline security assessment.



// get started

Work with AgentOffense

Tell us about your target and goals. We’ll reply with scope and a fixed-price quote — usually within one business day.

./request_engagement