Pentesting the Agent: Where AI Workflows Actually Break
// elusive thoughts // ai security // agentic // stop testing the model, test the wiring
Most people who say they are pentesting an AI feature are testing the wrong thing. They open the chat window, they type ignore all previous instructions, they get the model to say something rude, they screenshot it, and they file a finding about prompt injection. That is not a pentest. That is a party trick, and it teaches the developers that AI security is about naughty words, which is exactly the lesson you do not want them to learn.
The interesting target was never the model. It is the wiring around it. Somewhere between the user and the language model there is a system that retrieves documents, calls tools, holds credentials, and renders output into a browser. That system was assembled quickly, by people under deadline, on top of a component nobody in the building fully understands. That is where you break in. The model is just the confused employee you socially engineer to do it.
The mental model that makes findings fall out
Draw the thing as it actually is. Not a magic oracle. An untrusted, highly persuadable component that sits inside your trust boundary and holds credentials. The moment you draw it that way, the whole test plan writes itself, because you have seen this shape before. A confused deputy with a keyring is a thirty year old problem. It just arrived this year wearing a hoodie and calling itself an assistant.
the pattern
Assume the injection succeeds. Do not spend the engagement trying to prove you can inject. You can. Everybody can. Spend it proving what the injection reaches. Prompt injection is not the vulnerability. It is the unauthenticated foothold. The vulnerability is everything the foothold can touch.
Road one: the model has more access than the user driving it
The first thing to enumerate on any agent is whose authority it acts with. Nine times out of ten the answer is a service account, because that was the easy way to ship, and the service account can read across tenants because scoping it properly was a later ticket that never came.
Now you have your attack. You do not need to break the model. You need to get untrusted text in front of it and let it use the badge it already holds. A document you upload. A calendar invite. A support ticket. A web page the agent is asked to summarise. Any channel where content you control becomes content the model reads is an instruction channel, and the model will happily act on it with permissions the human sending the request never had.
The test is simple. Plant an instruction in a document that only your low privilege user can see, ask the agent an innocent question, and watch whether it reaches data your user cannot. If it does, you did not find an AI bug. You found a privilege escalation that happens to have a chatbot in the middle, and it should be written up with exactly that severity.
Road two: the retrieval layer never checked permissions
Retrieval augmented generation is where the quiet breaches live. Somebody embedded the company's documents into a vector store so the assistant could answer questions about them. Ask whether the retrieval query respects document level permissions, and the honest answer is often that the permission check happens at display time, after the content has already been pulled into the model's context.
Think about what that means. The model has already read the document the user is not allowed to see. The access control is a curtain drawn after the safe was opened. And the model does not honour curtains. Ask it the right question and it will summarise for you, cheerfully, the contents of files your account has no rights to, because as far as the retrieval layer was concerned every embedding in the index was fair game.
To test it you need two users and one shared store. Index a secret as the privileged user. Log in as the other one. Ask around the secret rather than for it. If the summary contains something your account cannot open directly, the permission model lives in the wrong place and you have a real data exposure.
Road three: the output is rendered, and rendering executes
This is the road pentesters miss because it does not look like AI at all. It looks like markdown working as intended.
The model returns markdown. The frontend renders it. Markdown supports images. Images have URLs. URLs carry query strings. Put those four facts together and you have a data exfiltration channel that fires with no click.
An instruction buried in a retrieved document tells the model to end its answer with an image whose address is an attacker server followed by a summary of the conversation. The frontend renders the image tag. The browser fetches it automatically, and the fetch carries the stolen content in the URL. Nobody clicked anything. The user watched a helpful answer appear and a beacon left the building underneath it.
field note
The best AI finding I have ever written was old fashioned cross site scripting thinking pointed at a new output channel. No clever jailbreak. Just markdown, an image tag, and a frontend that trusted the model's output the way we all learned twenty years ago never to trust user input. The model is user input now. Treat everything it emits as attacker controlled, because with one injected instruction it is.
Road four: the tools have verbs they should not
Once an agent can call tools, enumerate them like an API, because that is what they are. For each one ask two questions. What does it do, and what does it do with attacker supplied arguments.
The dangerous pattern is a tool with a side effect and no confirmation. Send email. Delete record. Post to channel. Make outbound request. If the model can call those and the model can be steered by injected text, then the injected text can send the email, delete the record, and make the request to wherever it likes. The classic escalation is a tool that fetches a URL, because that is your exfiltration path and your server side request forgery in one, handed to you by the feature itself.
The test plan is boring and effective. Scope every tool. Inject an instruction that calls the most dangerous one with arguments you control. See if a human confirmation stands between the model and the action. If nothing does, the blast radius of a single planted sentence is the full set of verbs you just enumerated.
Road five: nobody can tell you what it did
End every agent engagement with one request. Show me the logs of what the model actually did during my testing. Which tools it called, with which arguments, against whose data, and what came back.
Most of the time the log says the assistant was invoked and nothing else. That is a finding on its own, and a serious one, because it means that when a real attacker walks these same roads the incident response will be a shrug and a sentence that begins we think it did something. An agent action is a privileged action. If you would log an admin deleting a user, you log the model deleting a user, with the same fields, or you are flying blind on purpose.
What to hand the developers
Not a jailbreak transcript. Give them the boundary decisions, because those are the fixes.
Collapse the model's identity into the caller's. Every tool call runs with the user's token through the same authorisation layer as the rest of the product. The model may ask for anything. The authz layer decides what it gets.
Move the permission check to query time, inside the retrieval store, before anything reaches the context window. Slower and correct beats fast and leaking.
Treat model output as untrusted. Allowlist rendered image and link domains. No auto loading of external resources. The same discipline you would apply to any user generated content, because that is now what it is.
Put a human in front of every side effect, and allowlist outbound calls from the tool layer.
Log every tool invocation as a first class audit event. Actor, tool, arguments, authorisation decision, result size. The same as any other privileged action, because it is one.
The reframe
The OWASP list for large language models is a fine place to start and it will not save you, because every finding on these five roads is a classic problem in an unfamiliar costume. Confused deputy. Missing authz on the read path. Output encoding. Server side request forgery. Insufficient logging. We have known how to test all of these since before the model existed.
Stop trying to out argue the model. You will lose, it has read more than you and it never gets tired. Draw it as what it is, a persuadable insider with a keyring, and test the keyring. The magic is not in the model. It never was. The magic was always in how much access somebody handed it on a Friday afternoon to make the demo work.
// Elusive Thoughts // the model is the mark, not the target // securityhorror.blogspot.com
Techniques described are for authorised testing of systems you own or have permission to assess. Analysis and commentary are my own.
#AIsecurity #LLMhacking #AgenticAI #PromptInjection #RAG #AppSec #PenTest #OWASP