Penetration Testing LLM-Powered SaaS Features and API Endpoints

LLM-powered features don't just expand your attack surface. They fundamentally change its nature. Conventional web application penetration testing operates on a straightforward assumption: inputs flow into deterministic logic and produce predictable outputs. An LLM endpoint breaks that assumption entirely, because the processing layer isn't executing code, it's interpreting natural language and acting on it. That distinction means attacker-controlled text can become attacker-controlled behavior, and most standard pen testing checklists have no framework for that. OWASP recognized this clearly enough to publish a dedicated LLM Top 10, separate from the Web Application and API Top 10s, and that's not a bureaucratic gesture. It signals that the threat class has matured to the point where practitioners have reached consensus. If your team is shipping LLM-powered SaaS features and running only conventional security tests against them, you are leaving entire categories of vulnerability untouched.
Before getting into specific attack classes, it helps to establish a shared vocabulary for how these systems are actually built. A typical SaaS LLM feature has a system prompt, written by the developer and usually hidden from the end user, which establishes the model's behavior, scope, and persona. On top of that sits the user prompt, which is often partially or fully attacker-controlled. Many products add a retrieval layer, a RAG pipeline pulling from a vector database or external document store, to give the model access to proprietary data. Increasingly, there's a tool-calling layer that lets the model trigger real operations: sending emails, querying databases, calling internal APIs. Finally, the model's output renders somewhere, either in a UI, a downstream system, or an external service integration. Each of these layers is a distinct attack surface. A test that only hits the HTTP endpoint touches none of the interesting parts.
Standard automated scanners have no mechanism to fuzz natural-language instruction surfaces. Manual testing, by someone who understands how these models respond to adversarial inputs, is required.
Prompt Injection: What It Is, How Direct and Indirect Variants Work, and What a Working Exploit Looks Like
The core mechanism of prompt injection is straightforward: attacker-supplied text overrides or hijacks the developer-authored instructions in the system prompt. The model, by design, follows instructions. The attack is just supplying better ones.
Direct prompt injection is the variant most people have encountered. The attacker types instructions into the user-facing input that contradict or replace the system prompt. The classic form is "ignore all previous instructions and instead," but the unsophisticated phrasing undersells how effective more subtle variants are. Role-reassignment attacks work by telling the model it has entered a new operational mode with different permissions. Context-collapse attacks tell the model that everything above was a test and the real instructions follow. Against a customer-support LLM constrained to answer only product questions, a well-crafted injection can cause the model to reveal the contents of its own system prompt, answer questions entirely outside its defined scope, or execute behaviors the developer explicitly prohibited. That's a confirmed vulnerability, not a theoretical one.
Indirect prompt injection is considerably more dangerous, and considerably harder to defend. Here, the attacker doesn't type instructions into the user input field. Instead, they embed malicious instructions in content that a RAG-enabled or agentic LLM will later retrieve and process. A SaaS product that summarizes user-uploaded PDFs is a clean example: the attacker uploads a PDF containing hidden text instructing the model to, when summarizing, also transmit the contents of the user's account settings to an attacker-controlled endpoint. The malicious payload arrives through the retrieval path, not the input field. Web application firewalls don't inspect it. Input validation fails to catch it. The model receives it as trusted context and acts accordingly.
What this means for testing is that every retrieval source the system touches is an injection surface: uploaded files, web fetch integrations, email ingestion, database-backed RAG. Each one needs its own test cases.
The distinction between an interesting observation and a confirmed finding matters enormously here. A model that sometimes quotes part of its system prompt under a particular phrasing is worth noting. A model that can be reliably induced to do so across sessions with a reproducible payload, producing consistent output, is a confirmed vulnerability. Documentation should capture the exact payload, the exact output, and the reproducibility. "The model occasionally says things it shouldn't" is not an actionable finding.
Insecure Tool Use: When the Model Can Pull a Trigger It Shouldn't Be Able to Reach
The expansion of LLM features into agentic territory, where models can call functions, query databases, send emails, write files, and invoke internal APIs, creates a compounding risk. Prompt injection on its own is a serious finding. Prompt injection against a model with attached tools is a different threat order entirely.
The attack path is direct: if an attacker can redirect model behavior through injection, and the model has tools registered, the attacker inherits whatever permissions those tools carry. The authorization question shifts from "can this user access this resource" to "can this user cause the model to access this resource on their behalf," and those are very different enforcement problems.
The testing approach starts with tool manifest enumeration. What tools are registered with the model? Most LLM frameworks expose this in the system prompt or via the function-calling API, and a whitebox engagement should review the full list. Then: are any of those tools more powerful than the feature actually requires? The principle of least privilege applies here exactly as it does to service accounts. A model given database write access when the feature only requires reads is a misconfiguration, and it's a common one because teams configure tool access permissively during development and fail to revisit it before shipping.
The working exploit that illustrates this class most concretely is an AI coding assistant with read access to a code repository and the ability to open pull requests. An attacker who can embed indirect injection instructions in a code comment can cause the model to open a PR inserting a backdoor. The developer reviewing that PR may not immediately recognize what triggered it. The injection payload is in source code. The execution is a legitimate tool call. The damage is real.
OWASP's LLM Top 10 2025 addresses this pattern under excess agency: LLM systems granted more permissions, more tool access, or more autonomy than the feature actually requires. Every excess permission is unnecessary blast radius. Testing should include an explicit permission scope review, attempts to trigger out-of-scope tool calls via injection, and verification that tool execution logs exist and are being monitored.
Human-in-the-loop confirmation before destructive or sensitive tool calls execute is a meaningful control. Its presence or absence should be documented in every engagement.
Data Exfiltration Through Model Outputs: How Sensitive Information Leaks Without a Single SQL Query
The exfiltration risk specific to LLM features doesn't look like a traditional data breach. There's no SQL injection, no unauthorized API call returning a structured response, no file downloaded through a misconfigured S3 bucket. The leak comes out of the model's response in natural language, and most data-loss prevention tooling isn't reading natural-language outputs for secrets.
There are three distinct exfiltration mechanisms worth separating.
The first is training data leakage: a model fine-tuned on sensitive internal data may have memorized portions of that data and reproduce it in response to structured probing. This is harder to remediate than a conventional code vulnerability because the fix isn't a patch, it's retraining or output filtering. Testing involves probing for known-sensitive strings, PII patterns, and internal identifiers that should never surface in model outputs.
The second is RAG-layer exfiltration, which is more prevalent in production SaaS products. The model has retrieval access to a document store, and attacker prompts cause it to surface content it has no business surfacing. The failure mode to test for is cross-tenant retrieval in multi-tenant architectures: a shared vector store with metadata-based filtering, where an attacker prompt semantically matches another tenant's documents and bypasses the filter through the model's response. That is a cross-tenant data leak executed entirely through natural-language queries. It doesn't look like any conventional vulnerability class, which is precisely why it gets missed.
The third is prompt exfiltration, a specific application of prompt injection. System prompts are routinely treated as sensitive by development teams; they often contain business logic, internal API endpoints, behavioral constraints, and occasionally hardcoded credentials. They are not cryptographically protected, however. They exist in the model's context window, and a successful injection can extract them verbatim. The fact that a system prompt is "hidden" from the UI is not a security boundary.
Agentic output channels create a fourth path worth noting. In systems where model outputs are routed to external services, an attacker who can control output content may be able to exfiltrate data through a rendered markdown image tag pointing to an external URL, a webhook the model constructs, or a URL embedded in a model-generated response that a downstream system follows. These channels deserve explicit mapping and testing.
Broken Access Controls on Inference Endpoints and Multi-Tenant Isolation Failures
LLM inference endpoints are APIs. They inherit every conventional API access control vulnerability, and teams frequently treat them as something separate, an "AI feature" that lives outside the normal security review cycle. That treatment is the vulnerability.
The conventional failures show up predictably: inference endpoints accessible without a valid session token, authorization enforced at the UI layer but not on the API itself so direct calls bypass role restrictions, rate limiting absent or trivially bypassable, and IDOR on conversation or session IDs where incrementing an integer exposes another user's chat history. None of these are novel. They appear on LLM endpoints for the same reason they appear anywhere: the endpoint was built quickly, treated as internal, and never formally reviewed.
Multi-tenant isolation introduces failure modes more specific to SaaS LLM architectures. Each tenant's context, their data, their system prompt customizations, their conversation history, must be strictly isolated. The failure modes include shared vector stores with weak metadata filtering, shared conversation memory across tenants, and system prompt customizations from one tenant leaking into another's session. The test methodology is straightforward: maintain two separate tenant accounts and attempt to access or influence one from the other, both through direct API calls and through model-layer manipulation. Both paths need to be tested independently because they can fail independently.
The model-as-a-service proxy pattern deserves specific attention. Most SaaS products don't run their own foundation models; they proxy calls to an upstream provider and hold the API key server-side. If that proxy layer has SSRF vulnerabilities or parameter injection weaknesses, an attacker may be able to redirect inference calls, manipulate model parameters directly, or substitute their own system prompt. The API key itself is also an asset worth examining: if it can be extracted from a misconfigured endpoint, the attacker now has direct access to the underlying model billed to the vendor.
Cost and quota exhaustion doesn't constitute a data breach, but it represents a genuine business risk. An unauthenticated or inadequately rate-limited inference endpoint can be abused to run up substantial API costs or exhaust a tenant's usage quota. It belongs in scope and in the report.
How the RAG Pipeline and Vector Database Expand the Attack Surface Beyond the Model Itself
RAG pipelines are how most production SaaS LLM features give a model access to proprietary or current data without fine-tuning. The model retrieves relevant chunks from an external knowledge base at query time and uses them as context. It's an elegant architecture. It also introduces an attack surface that extends well beyond the model itself and well beyond what a test focused only on the chat interface can reach.
Vector databases, the specific infrastructure underlying most RAG implementations, tend to be less hardened than relational databases. The tooling is newer, operator familiarity is lower, and security defaults are less mature. The first tests to run against a RAG-backed feature are fundamental: is the vector store accessible without authentication? Can stored embeddings be enumerated? Can vectors be inserted or modified by an attacker? That last capability enables embedding poisoning.
Embedding poisoning is a supply-chain-style attack on the model's context window. In a product where users can contribute to a shared knowledge base, an attacker submits a document containing indirect injection payloads. That document gets chunked, embedded, and stored. Whenever a related query triggers retrieval of that chunk, the injection payload lands in the model's context for every user whose query matched it. The attacker doesn't need ongoing access. The payload is persistent in the retrieval pool.
Chunking and metadata handling is where cross-tenant isolation often fails silently. Most RAG implementations attach metadata to each chunk, including tenant ID, document owner, and access level, and rely on filtering by this metadata at retrieval time. The question to test is whether those filters hold at the vector similarity layer or only at an application layer that can be bypassed. Semantically crafting a query to match another tenant's documents and observing whether the metadata filter prevents retrieval is a test that takes minutes to design and can surface a critical finding.
The document ingestion pipeline is its own attack surface, distinct from the retrieval layer. File parsing for PDFs and DOCX files introduces standard parsing vulnerabilities. URL-based ingestion, where the product fetches documents from user-supplied URLs to embed, introduces SSRF: that fetch can be redirected to internal services, metadata endpoints, or other resources not intended to be reachable from outside the network.
A test that covers only the chat interface has not tested the RAG pipeline.
What a Structured Test Plan for an LLM-Powered Feature Actually Covers
The starting point is threat modeling the specific feature before any testing begins. Map every component: inference endpoint or endpoints, tool registrations, retrieval pipeline, vector store, output rendering, downstream integrations. Identify trust boundaries: what does the model treat as authoritative instruction? What data sources does it access? What can it write or trigger? Identify every attacker-controlled input, direct user prompt, uploaded files, fetched URLs, third-party data the model retrieves. The map comes first. Testing without it means guessing at scope.
Whitebox access matters more in LLM testing than in most conventional engagements. System prompts, tool definitions, RAG pipeline configuration, and embedding logic are not visible from outside the application. A blackbox test of an LLM feature is severely constrained because it can't see what instructions the model is operating under, what tools it has access to, or how retrieval is filtered. Requesting whitebox access isn't a preference; it's a prerequisite for a test that covers the actual threat surface.
The test case categories, structured across the components mapped in the threat model, cover the following. Prompt injection: direct via user input fields, indirect via each retrieval source the system touches, and multi-turn, where injection payloads are spread across conversation turns to evade single-turn filters. System prompt extraction: multiple phrasing variants, context manipulation, encoding tricks, and role-reassignment attempts. Tool abuse: enumerate available tools, attempt to invoke out-of-scope tools, test authorization on each tool call independently, and verify that destructive tool calls require confirmation. Data exfiltration: cross-tenant retrieval via RAG, PII extraction from model outputs, system prompt extraction, and output channel abuse in agentic features. API access controls: authentication and authorization on every inference endpoint, rate limiting configuration, IDOR on session and conversation identifiers, and tenant boundary enforcement. RAG and vector database: authentication on the vector store, embedding poisoning via user-contributed content, metadata filter bypass, and SSRF via URL-based ingestion.
Every finding requires a reproducible payload and a documented impact. The difference between a vulnerability and an observation is reproducibility and consequence. "The model can be prompted to behave unexpectedly" is not a finding. "This payload, submitted to this endpoint, reliably extracts the system prompt contents, confirmed across five independent sessions" is. The standard of evidence here is the same as everywhere else in security testing, and it's worth holding to it precisely because LLM behavior has a stochastic quality that makes it tempting to document impressions rather than confirmed exploits.
The threat class is mature. The tooling to test it is mostly manual, and the practitioners who can do it well are still relatively scarce. That combination means the gap between what teams are shipping and what they're testing remains wide. Closing it starts with understanding that the model is not a black box to route around. It is the attack surface.


