Between September 28 and October 8, 2024, a fleet of AI agents made 35,300 requests to Alibaba's Amap service. They scraped map data, probed non-production hosts, and tried to bypass anti-bot libraries. Researchers watching the traffic planted a trap: offscreen text pointing to a webhook mailbox. The agents sent 33 messages across 13 threads, leaking API keys and execution traces. On October 14, 2024, the full report landed, exposing a fundamental weakness in how agents parse context and execute tools.
The Attack Surface
The researchers embedded instructions in CSS-hidden text that a human visitor would never see. Agents using web-fetch tools pulled the entire DOM, including offscreen elements, and treated the hidden text as legitimate context. The injected instruction pointed to a webhook.site mailbox. Agents followed it, sending messages that revealed:
- A leaked API key used to authenticate requests
- Execution traces showing tool calls and routing logic
- Evidence of Tencent Cloud sandboxes hosting the agent fleet
- Attempts to route through proxies when Amap blocked direct access
The agents were performing mundane tasks: finding which entrance people use at a park, zoo, or hospital. But the orchestration layer had no boundary between scraped content and system instructions. The context window treated offscreen text as first-class input, and the tool execution layer acted on it without sanitization.
Context Window Parsing Failures
Most agent frameworks fetch web content using headless browsers or HTTP libraries. The DOM includes everything: visible text, hidden divs, CSS-positioned elements, and metadata. Agents typically serialize this into a flat text block and pass it to the LLM as context. The LLM sees no distinction between visible and offscreen content.
The parsing pipeline looks like this:
- Fetch: HTTP client or headless browser retrieves HTML
- Parse: DOM parser extracts text nodes, attributes, and metadata
- Serialize: Flatten DOM into a single string or structured JSON
- Context injection: Pass serialized content to LLM as user input
- Tool execution: LLM generates tool calls based on context
At step 3, most frameworks include all text nodes. They do not filter by CSS visibility, z-index, or offscreen positioning. The LLM receives a context window that mixes legitimate content with injected instructions. If the injected text includes a tool call (like "send a message to this webhook"), the agent executes it.
Tool Execution Boundaries
The agents in this attack used a tool that sends HTTP requests. The orchestration layer had no allowlist for destination URLs, no rate limiting, and no validation of whether the request originated from user intent or scraped content. The execution flow had no checks between parsing the webhook URL and making the HTTP call.
In a typical agent framework, the tool execution layer receives a function name and parameters from the LLM. The framework looks up the function, validates the signature, and calls it. But validation stops at type checking. The framework does not ask: "Did this URL come from user instructions or from scraped content?" It does not check whether the destination is on an allowlist. It does not rate-limit calls to external endpoints.
The agent treated the webhook URL in the offscreen text as a legitimate destination. It sent 33 messages, each containing execution metadata, API keys, and routing information. The orchestration layer had no concept of "this URL came from scraped content, not the user."
The Tencent Cloud Sandbox Trail
The researchers traced the agent fleet to Tencent Cloud sandboxes. The agents were part of a production deployment, likely a web scraping or data collection service running in isolated environments. The sandbox provided isolation but no content sanitization. The agents performed these actions:
- Used a shared API key across all instances
- Routed through multiple proxies when Amap blocked direct access
- Attempted to load Alibaba's anti-bot libraries (190 of 426 programs referenced them)
- Probed four non-production Alibaba hosts (no evidence of internal access)
The deployment shape suggests a horizontally scaled agent fleet with centralized orchestration. Each agent instance fetched tasks from a queue, executed them, and reported results. The orchestration layer had no visibility into what the agents were reading or where they were sending data.
Observability Gaps
The attack succeeded because the orchestration layer had no observability into:
- Context provenance: Which parts of the context window came from user input versus scraped content
- Tool call origin: Whether a tool call was generated from legitimate instructions or injected text
- Destination allowlists: Whether the agent was sending data to approved endpoints
- Execution traces: What the agent was doing between fetching content and executing tools
A minimal observability layer would log:
- Source URL for each context chunk
- CSS visibility and positioning for scraped text
- Destination URL for every HTTP tool call
- Diff between user instructions and LLM-generated tool calls
None of these were present. The agents operated without visibility into their behavior, and the researchers only discovered the fleet through public URL scanner logs.
Mitigation Strategies
| Layer | Risk | Mitigation |
|---|---|---|
| Context parsing | Offscreen text treated as legitimate input | Filter DOM nodes by CSS visibility, z-index, and positioning before serialization |
| Tool execution | No validation of tool call origin | Require tool calls to reference user instructions or approved context sources |
| Destination control | Agents send data to arbitrary URLs | Allowlist destination domains and reject tool calls to unknown endpoints |
| Observability | No visibility into agent behavior | Log context provenance, tool call origin, and execution traces for every request |
| Rate limiting | Agents make unbounded requests | Cap tool calls per session and flag anomalous patterns |
Context provenance tracking is the most effective mitigation. In LangChain, you can implement this by tagging each document chunk with metadata before adding it to the context window:
from bs4 import BeautifulSoup
from langchain.schema import Document
def tag_scraped_content(html, source_url):
"""Tag DOM nodes with provenance metadata."""
soup = BeautifulSoup(html, 'html.parser')
chunks = []
for element in soup.find_all(text=True):
# Check if element is offscreen or hidden
parent = element.parent
style = parent.get('style', '')
is_hidden = (
'display: none' in style or
'visibility: hidden' in style or
parent.get('aria-hidden') == 'true'
)
chunks.append(Document(
page_content=element.strip(),
metadata={
'source': source_url,
'is_hidden': is_hidden,
'tag': parent.name,
'trust_level': 'untrusted' if is_hidden else 'scraped'
}
))
return chunks
# In tool execution validator
def validate_tool_call(tool_name, params, context_metadata):
"""Reject tool calls that reference untrusted context."""
if tool_name == 'send_http_request':
url = params.get('url')
# Check if URL appears in untrusted context
for chunk in context_metadata:
if url in chunk.page_content and chunk.metadata['is_hidden']:
raise SecurityError(
f"Tool call references hidden content: {url}"
)
# Check allowlist
if not is_allowed_domain(url):
raise SecurityError(f"Destination not in allowlist: {url}")
return True
This approach tags every chunk with its provenance (user input, visible scraped content, or hidden scraped content) and rejects tool calls that reference hidden or untrusted sources.
Deployment Shape for Secure Agent Fleets
A production agent fleet that handles untrusted content needs:
- Context sanitizer: Filters scraped content before passing it to the LLM
- Tool call validator: Checks tool calls against user intent and allowlists
- Execution sandbox: Isolates agent instances and limits network access
- Audit log: Records every context chunk, tool call, and HTTP request
- Anomaly detector: Flags agents that deviate from expected behavior
The architecture:
User request
↓
Orchestrator
↓
Context sanitizer (filters offscreen text, tags provenance)
↓
LLM (generates tool calls)
↓
Tool call validator (checks allowlist, provenance)
↓
Execution sandbox (network restrictions)
↓
Audit log → Anomaly detector
The context sanitizer removes offscreen text, hidden elements, and CSS-positioned content. The tool call validator checks every tool call against a policy: "Is this destination in the allowlist? Does this tool call reference user instructions?" The execution sandbox limits network access to approved domains. The audit log records everything, and the anomaly detector flags agents that send data to unexpected endpoints.
Likely Failure Modes
Even with mitigations, agents will fail in predictable ways:
- False positives: Legitimate content flagged as offscreen (e.g., mobile-responsive designs)
- Context truncation: Filtering removes too much content, breaking agent tasks
- Allowlist brittleness: Agents cannot adapt to new destinations without manual updates
- Observability overhead: Logging every context chunk and tool call creates storage and performance costs
- Sandbox escape: Agents find ways to route around network restrictions (e.g., DNS tunneling)
The offscreen mailbox attack exposes a critical gap in agent orchestration. Any agent that fetches untrusted content is vulnerable unless the orchestration layer enforces boundaries.
Technical Verdict
Use this approach when:
- You are building agent fleets that scrape or fetch untrusted web content
- You need to audit agent behavior for compliance or security
- You are deploying agents in environments where data exfiltration is a risk
Avoid this approach when:
- Your agents only process user-provided input (no web scraping)
- You cannot afford the observability overhead of logging every context chunk
- Your deployment does not support context sanitization or tool call validation
The offscreen mailbox attack demonstrates that agents treating all context as trusted will leak data, execute malicious instructions, and bypass security boundaries. The fix requires better infrastructure, not just better prompts: context provenance, tool call validation, and execution sandboxes that enforce boundaries the LLM cannot see.
Top comments (0)