Back to  vulnerability database

CVE-2026-76066: Tool Visibility Not Enforced at Execution

A flaw in IntentKit's tool permissions lets an unauthenticated public caller run tools reserved for the agent's owner, including the always-present wallet transfer tool. Owner-only tools are hidden from the model but stay callable in the executor, so a call that names one still runs.

CVSS Score
7.5
Severity
High
Type
Authorization bypass
Attack type
Authorization bypass
Vendor/tech
crestalnetwork/intentkit
Known exploit
No
Impact Assessment
Confidentiality
Low
Integrity
High
Availability
None

Overview

IntentKit is an open-source framework for building autonomous AI agents, with built-in tools for crypto wallets, DeFi, social posting and memory. 

‍

Each tool can be marked public or private, and private tools are meant to be reachable only by the agent's owner. That restriction is applied only to the list of tools shown to the model.

‍

The executor that actually runs tool calls is built from the full private set, so a call to an owner-only tool still runs in a public context. An attacker who can get such a call to the executor, for example through indirect prompt injection in content the agent fetches or through history replay on a reused thread, can trigger wallet transfers, DeFi swaps, posts or memory writes, with no human approval step.

Because a public, non-owner caller of an IntentKit agent can get owner-only tools executed, including the always-present wallet transfer tool, with no approval step, deployments that expose agents to public users or untrusted content should treat it as high priority

‍

Affected Product

‍

crestalnetwork/intentkit

‍

Affected Files

  • intentkit/core/executor.py, lines 121-251 (tool-list assembly) and 341-349 (create_langchain_agent(..., tools=private_tools, ...))
  • intentkit/core/middleware.py, lines 71-90 (ToolBindingMiddleware.awrap_model_call)
  • intentkit/core/engine/stream.py, lines 280-291 (is_private assignment)
  • intentkit/tools/base.py, lines 49-63 (is_tool_visible)

‍

Root Cause

Tool visibility is advertise-only and not enforced at the executor. executor.py builds the agent with create_langchain_agent(model=..., tools=private_tools, ...), so the LangGraph ToolNode is constructed from the full private superset: public tools plus owner-only state == "private" tools plus system tools such as cdp_native_transfer, create_post, update_memory, and call_agent.

‍

ToolBindingMiddleware.awrap_model_call selects private_tools when context.is_private else public_tools and calls request.override(tools=...), which narrows only the model-facing advertisement, not the executor. stream.py sets is_private=True only for the agent owner, a matching team, or the local system user, so entrypoints WEB, API, TWITTER, and TELEGRAM with user_id not equal to the agent owner get is_private=False. In a public context the executor validates a tool call against its full registry rather than the narrowed advertisement, so a call for an owner-only tool that reaches the executor still runs.

‍

No tool has an interrupt_on, human-in-the-loop, or approval gate, so a sensitive tool executes immediately once called.

‍

PoC

A local harness reusing IntentKit's wiring (create_langchain_agent(tools=private_tools) plus ToolBindingMiddleware), with a benign id canary standing in for an owner transfer. 

‍

Impact

An operator who marks a state-changing tool private, expecting owner-only reachability, finds it executable for a public caller whose call reaches the executor, for example via indirect prompt injection supplying the exact tool name and arguments, or checkpoint and history replay on a reused thread (thread_id = agent_id-chat_id).

‍

Any deployment that exposes an agent with sensitive tools to untrusted or retrieved input, such as public or shared agents, Twitter or Telegram bots, retrieval and web-fetch workflows, and autonomous tasks, allows indirect prompt injection to reach owner-only capabilities (wallet transfers, DeFi swaps, posting, memory writes) with no execution-time guard. cdp_native_transfer is one of the always-present system tools folded into every agent's private superset, so a wallet tool is reachable by default rather than by operator opt-in.

‍

Suggested Remediation

  1. Enforce tool availability at the executor, not only the advertisement: build the ToolNode from the same context-filtered set the model is shown, or reject a tool call whose name is not in the current public or private advertisement for that turn.
  2. Add a human-in-the-loop or approval gate for state-changing tools (wallet transfers, DeFi, posting), at minimum for turns whose context includes retrieved or untrusted content, and default it on for public or shared agents.
  3. Treat retrieved and inbound-message content as data, never instructions, and ignore tool directives originating from retrieved content.

‍

Additional Resources

  • CVE-2026-76066 record: link to be added

Automated AI Red Teaming

Test your applications against adversarial conditions.
Book a Demo