Hi people,

AI agents become much more interesting when they can use tools.

A model that only generates text is one security boundary. An agent that can retrieve data, connect to services, and perform actions is a different system entirely. The model may decide what to do, but the surrounding application decides which tools exist, how they start, and what privileges they receive.

That surrounding application was where I found this issue.

I was testing a custom-agent workflow inside a researcher-controlled organization. The platform supported Model Context Protocol tools, and its normal connection flow appeared to understand an important rule: customer configuration should not be allowed to start an arbitrary local process on the provider's infrastructure.

Then I found another path that did not enforce the same rule.

Publication note: This story is reconstructed from private research. The organization, product, routes, schema names, dates, identifiers, commands, runtime versions, and implementation details were removed or replaced. Every example below is synthetic. "Critical/P1" describes the submitted severity, not a final program decision. I am not claiming a deployed fix, completed retest, or program acceptance.

A draft AI agent launching a local MCP tool inside a privileged server runtime.
Original 3D glass-and-chrome editorial illustration for this anonymized Critical/P1 field note.

The safe path said no

I started with the platform's ordinary tool-import workflow. I supplied a harmless synthetic configuration representing a process-backed MCP tool. The importer rejected it because that workflow supported remote connections, not local processes. That was the behavior I expected.

I also checked a more direct configuration path. That request was denied as well.

These were useful controls. One path explicitly rejected local process-backed tools. A separate direct-override path was access-controlled. In both cases, my test input did not reach execution.

At that point, the result looked reassuring. But a control protects only the paths that actually use it.

The draft agent accepted what the importer rejected

The application also allowed organization administrators to create custom AI agents. A custom agent could contain instructions, procedures, and tool definitions.

I created an empty draft agent in my controlled organization. It was never published or shared with another user. I then updated that draft with one synthetic MCP tool. The tool was deliberately minimal: it could complete a protocol handshake, announce one harmless function, and return the identity of its own process.

The server accepted the tool configuration and returned it as part of the stored draft. This was different from the public importer, but persistence alone did not prove execution. The object could still have been inert data.

The next question was simple: would a draft agent actually run it?

Reconstructed evidence flow using placeholders. No original route, field, command, account, or runtime detail is shown.

A draft was not dormant

I started a new conversation with the draft agent. Creating the conversation caused the agent runtime to initialize its configured tools automatically. I did not need another user to open the agent, approve a tool, or interact with a malicious prompt. The ordinary start action was enough.

The completed state contained the full evidence chain: the run started, the configured tool initialized, the agent called it, the tool returned its process identity, and the run completed successfully.

PUBLIC IMPORTER  -> local process tool rejected
DIRECT OVERRIDE  -> forbidden
DRAFT AGENT      -> synthetic tool stored
THREAD START     -> tool launched automatically
TOOL RESULT      -> PRIVILEGED_RUNTIME_IDENTITY
RUN STATUS       -> completed

Those lines are descriptive placeholders, not original requests, fields, commands, or responses.

The result that changed the severity

The returned marker showed that the process was running with the operating system's root identity.

That result mattered for two reasons. First, it came from inside the MCP process itself. It was not a user-controlled string reflected by an API. The tool derived the marker from its own runtime and returned it through a completed MCP exchange.

Second, the process had been selected through tenant-controlled configuration. A normal customer-organization administrator had crossed the boundary from configuring an AI agent to launching a chosen process inside the provider-managed agent infrastructure.

This was no longer a prompt-quality problem. It was server-side code execution through an agent-tool configuration path.

Why the controls made the report stronger

It would have been easy to show only the successful agent run. The rejected paths made the conclusion much stronger.

The public importer rejected local process-backed tools. A direct override was forbidden. The draft-agent editor accepted the same dangerous class of configuration. That contrast isolated the problem as a validation mismatch between server-side entry points.

The system already knew the configuration was unsafe in one workflow. It simply failed to apply that policy everywhere an equivalent process-launch configuration could enter.

I keep seeing this pattern in complex products. A security rule lives inside one controller, importer, or interface instead of being enforced at the final trust boundary. A second feature later accepts the same data through a draft editor, clone operation, or internal workflow. The dangerous object reaches execution because every layer assumes another layer already validated it.

What I deliberately did not test

Root process execution creates many follow-on questions. Could the runtime read application files? Did it hold service credentials? Could it reach internal services? Was another tenant accessible? Could the process escape its isolation boundary?

I did not test any of them.

The proof ran once, completed the MCP handshake, returned its own identity, and stopped. It did not:

  • Start a shell or child process.
  • Read files, environment variables, secrets, or credentials.
  • Make internal, external, or metadata-service network requests.
  • Access another tenant.
  • Attempt persistence, container escape, or host access.

After collecting the minimum evidence, I removed the tool configuration, archived my controlled agent and session, and verified that no test connection state remained.

That restraint did not weaken the finding. A researcher-selected process launched with a root identity was already the security boundary I needed to prove.

Why I submitted it as Critical/P1

The required access was an authenticated administrator of a normal customer organization. It did not require provider staff access, infrastructure credentials, an unauthenticated entry point, or interaction from a victim.

From that tenant-level position, the demonstrated behavior crossed into the provider-managed agent runtime and started a researcher-selected process as root.

The proof established that primitive directly. Filesystem visibility, service credentials, internal-network reachability, workload manipulation, and cross-tenant impact were potential consequences of the primitive, not actions I claimed to have performed.

I submitted it as Critical/P1 because tenant-controlled configuration caused a privileged server-side process launch, not because I inflated untested follow-on scenarios.

The real fix belongs at the execution boundary

Blocking one field or hiding the local-tool option in the interface would not solve this class of issue.

Local process transports and process-launch fields should be rejected at every server-side configuration boundary: create, update, import, clone, draft, publish, and run. The normalized object should then be checked again immediately before execution.

Tenant data should select from provider-owned tool types instead of carrying an arbitrary executable and its arguments. Agent tools should run in short-lived, non-root sandboxes with a read-only filesystem, strict resource limits, no inherited application credentials, and no unnecessary internal-network access. Draft agents need the same checks as published agents before they can start.

Open-source workflow companion: Agent Scan

One open-source companion I would add to this workflow today is Snyk Agent Scan. It discovers agent components, MCP servers, and skills, then checks them for risks such as prompt injection, unsafe data handling, and hidden payloads. For this testing pattern, I would use it in an isolated lab to inventory every declared tool before manually comparing what the import, draft, clone, and run paths accept. Its own documentation warns that inspecting an MCP configuration may start a local server or make a network request, which is exactly why configuration must be treated as executable input. The scanner does not replace the manual server-side boundary proof in this story. This is a current workflow recommendation, not a claim that Agent Scan was used in the original private submission.

Takeaway

The most important lesson was not that MCP is inherently unsafe. It was that tool configuration is executable input.

When an AI product lets customers attach tools, connectors, functions, or agents, I now trace the complete path:

  • Who can define the tool?
  • Where is the configuration validated?
  • Can another endpoint store the same object?
  • Can drafts execute?
  • What starts the runtime?
  • Which identity runs it?
  • What can that runtime reach?

The model was not the vulnerability here. The failure lived in the orchestration layer around it: one path rejected a dangerous tool, another stored it, and the runtime trusted what reached the end.

That is all for this write-up. I hope it gives you a useful place to look when an AI feature says, "add your own tool."

For more on securing agent tools, retrieval systems, and model-connected workflows, see AI and LLM penetration testing. For another server-side trust-boundary story, read The File Importer That Stored Whatever the URL Returned, or browse the complete Bug Bounty Archive.

Test the orchestration layer

AI agent security extends beyond the prompt.

Balhence tests tool permissions, MCP integrations, agent workflows, retrieval boundaries, and the infrastructure controls around them.

Research topics: AI agent security / MCP security / command injection / remote code execution