Yaamlabs

AI security

SalesBleed: a poisoned lead hijacks a Salesforce AI agent

Three flaws in Salesforce Agentforce, found by Zenity Labs, let a hidden prompt in a public lead form leak CRM data over DNS and send phishing messages through Slack.

On September 24, Zenity Labs published SalesBleed, three vulnerabilities in Salesforce's Agentforce AI agent platform that let an outsider hijack a company's own AI agent using nothing but a public lead form. Two of the three exfiltrate Salesforce data without a click from anyone inside the company, and the third turns a trusted internal Slack bot into a phishing channel. Salesforce does not assign CVE numbers for Agentforce, since it runs the platform as a managed service, so there is no CVE to track, only Zenity's disclosure and Salesforce's fix.

This affects any organization running Agentforce with Web-to-Lead turned on, the standard embeddable form that lets any public website capture leads straight into the CRM. Zenity reported the flaws on June 1, and Salesforce closed the last of the three on September 21, finding no evidence anyone had exploited them beforehand. The same setup exists wherever an AI agent reads data submitted from outside the company and also holds permission to act on what it reads.

How it works

Web-to-Lead turns a few lines of HTML into a lead capture form that any company can drop onto its own website, and nobody has to log in to submit it. Whatever a visitor types lands straight in a new Lead record, in fields like Company, Description and Title, exactly as typed, and Agentforce treats that record like any other CRM data: when a sales rep asks the agent to summarize a new lead, the agent loads the whole record into its context and acts on what it finds.

That is the root of the bug: the agent's underlying model does not separate stored data from stored instructions. Zenity hid a prompt inside a Web-to-Lead field, and a routine employee request that made Agentforce load the poisoned lead let the buried text override the agent's actual task and tell it to query the Accounts table for company names and deal sizes, records the lead itself had no rights to but the agent's own session did.

Getting the data out relied on a second gap. Salesforce's Trusted URLs feature is meant to strip or block links to domains it does not recognize before an agent can act on them, but two edge cases slipped past it: a fixed allowlist of top-level domains that missed newer or unusual ones, and a mismatch between how the redaction filter and the rendering browser parse a URL's end, which let characters such as curly braces and brackets survive inside an HTML image tag. With redaction bypassed, the hijacked agent encoded the stolen company names and deal figures as a subdomain and rendered it in an image tag. Loading an image means resolving its hostname first, so the DNS query carrying that data reaches the attacker's own name server before any HTTP request is attempted, which is why this is zero click and hard to catch at the network edge: the data is already gone before an outbound web request would normally get inspected.

The third flaw lived in Agentforce's Slack integration. Its default Slack Knowledge subagent template ships a reply-to-thread action that, unlike other Slack write actions, needed no human confirmation and did not record which employee's request had triggered it. A hijacked agent could use it to post a phishing message in any Slack channel it could reach, the link hidden behind ordinary-looking text, appearing to come from the trusted bot identity employees already saw posting there. Slack automatically unfurls links to build a preview, so even a plain link fires an outbound request without a click.

What Zenity found

SalesBleed reached Salesforce through Zenity's responsible disclosure process, and Zenity found no sign of anyone using it against a real target before the fix landed. Its researchers, Alex Apostolov, João Donato, Avishai Efrat and Ayush RoyChowdhury, reported the chain on June 1. Salesforce hardened the Trusted URLs redaction and confirmed that fix on August 19, then closed the Slack action gap on September 21, adding attribution and a confirmation prompt.

This is the second time in about a year that the same intake point has produced a CRM-wide exposure. Noma Security disclosed a related but separate flaw, ForcedLeak, in September 2025, a CVSS 9.4 chain that also began with a hidden prompt in a Web-to-Lead field but exfiltrated through an expired domain Salesforce had left on its trusted list rather than through DNS. Neither case carries a CVE, since Salesforce does not issue them for flaws in a service it runs directly. What you have instead is a disclosure date, a fix confirmation, and a pattern worth checking your own setup against.

What to do

1. List every untrusted intake point that feeds an agent

Go beyond Web-to-Lead. In Setup, under Lead Settings, check whether it is enabled and which fields it writes to, then do the same for any other public form, chat widget or ticket queue whose content reaches a record an Agentforce or other AI agent will read. A field nobody reviews is an open channel into that agent's context.

2. Review what your agents can trigger without a person

Open each agent's topic and action configuration and find every action that posts, sends or exports without requiring approval: Slack replies, outbound email, data exports, record updates. Turn on confirmation for anything that leaves the CRM, and check Slack-connected templates specifically, since the default configuration Zenity tested shipped without it.

3. Check your logs for the pattern

In Event Monitoring, pull the Invocable Action Event log and look for a session that queries Accounts, or any table outside what the request needed, right after reading a newly created Lead. At the network edge, check DNS resolver logs for lookups to long or randomly-labeled subdomains in the minutes after. In Slack, check the audit log for messages from an agent's bot user with no human account attached to them.

4. Separate what the agent reads from what it can do

Give the part of the agent's job that reads untrusted input, parsing a new lead, a narrower session than the part that acts on trusted internal data, querying Accounts or posting to Slack. One broad session that can do both is what let a single poisoned form field reach two other systems, the same boundary you would already put around a public API endpoint that also carries admin scope on the backend.

Our AI-native systems engineers review what your agents are allowed to do once untrusted data reaches them, and set up the permission boundaries that catch this before an agent ships. Our red team and code review work tests this kind of chain, an intake point, an agent's permissions and its integrations, against your live setup rather than a diagram of it. Open the chat and Yaali, our AI agent, will pass your question to the engineer who would do the work.


Sources: Zenity Labs, SalesBleed: 0-click data exfiltration on Agentforce, Zenity Labs, SalesBleed: hijacking Agentforce in Slack for anonymous phishing, SecurityWeek, Infosecurity Magazine, Dark Reading, Noma Security, ForcedLeak.

Back to the blog, or read this post on the full site.