The Agent Read a Pixel. Your Laptop Ran a Command.
A malicious app paints a shell command onto the screen at 2% opacity. You can't see it. The agent's vision model reads it perfectly. The controller on your laptop runs it.
A malicious Android app paints the string test;pwd>rce_success onto your phone's screen at two percent opacity. You cannot see it. The screen looks completely normal.
Your mobile agent — running on your laptop, connected to your phone over ADB — takes a routine screenshot. GPT-4o reads the invisible text with 100% reliability across 20 trials. The agent's controller passes the string into subprocess.run(..., shell=True). Your laptop writes its working directory to a file. Remote code execution. On the operator's machine. Triggered by a pixel.
That's the finding from a paper called (A)I Sees What You Don't (arXiv 2607.00333), published July 1, 2026 by researchers at Simon Fraser, CUHK, Shandong University, and QAX's Xingtu Lab. Five of the biggest open-source Android agent frameworks — AppAgent, AppAgentX, Mobile-Agent-v3, Open-AutoGLM, and MobA — each failed at least six of seven attacks. As of writing, none of the vulnerabilities have been patched upstream. There are no CVE assignments.
I've been building agents on OpenClaw for eighteen months. This paper is the clearest articulation I've seen of a failure class I keep bumping into and can't stop thinking about: multimodal input has no provenance, and the confused deputy has your shell.
Two Attack Surfaces, Stacked
The paper carves out two novel attack surfaces. Each is bad on its own. Together they compose into host RCE.
Surface 1: The Perception Gap
Android's overlay system lets any app with SYSTEM_ALERT_WINDOW (or TYPE_APPLICATION_OVERLAY) draw pixels on top of everything else. Draw them at alpha = 0.02 and the human eye sees nothing. The framebuffer records them faithfully.
The researchers ran six vision-language models — GPT-4o, Claude Opus 4.5, Gemini 3 Pro, GLM-4V, plus two smaller — across 20 screenshots each. Four of them scored a perfect 20/20 at reading text drawn at 2% opacity. The weakest, an on-device 9B model, hit 18/20. Success doesn't increase as opacity rises. These models are already at the ceiling on human-invisible text. There's no lower bound where the perception gap closes.
There's a second variant that's even worse: hardware cutout injection. The Android framebuffer is a rectangle. The physical display is not — rounded corners and camera cutouts are masked by the bezel. Pixels rendered into those regions ship in every screenshot but are literally invisible on the glass. A Pixel 4 has about 78 pixels of hidden width in one corner. Enough for a short command string. All five agents parsed payloads out of it.
The paper is explicit on this one: "there is no straightforward and effective software-based solution." The framebuffer disagrees with the display, and no downstream contrast filter or OCR sanitizer can reconcile them without breaking legitimate UI rendering.
A third variant — chrominance-channel steganography — buries the payload inside an image's chroma channels. Luminance looks normal. The VLM decodes the hidden string. The victim's own agent screenshots the image out of a messaging app. No malicious app installed anywhere.
Surface 2: The Misused Channel
Perception attacks would be interesting but bounded if the model's output stayed on the phone. It doesn't. The paper's second contribution is documenting how phone-side text jumps the trust boundary to shell execution on the operator's laptop.
Four of five frameworks use a write-then-pull screenshot pipeline:
adb shell screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.pngMeasured gap between write and pull over 100 runs: 50–500 ms, mean 210 ms. Long enough for a background service polling every 5–10 ms to grab the file lock, repaint the PNG with attacker-controlled pixels, and release before the agent reads. Tampering succeeded 19 to 20 times out of 20 on four of five frameworks. Only MobA — which streams over exec-out and never writes a file — was immune to this TOCTOU.
Then the model does what a model does. It reads the doctored screenshot, notes the "instructions" the invisible text told it to follow, and returns something like test;pwd>rce_success. The controller in AppAgent's scripts/and_controller.py looks like this:
subprocess.run(adb_command, shell=True)
# builds: adb shell input text {input_str}The current shipped sanitizer strips spaces and single quotes. Nothing else. The metacharacters ;, &, >, |, backticks, and $() pass through untouched. So the shell that runs on the operator's laptop parses the string, sends the test part to the phone, and executes pwd>rce_success locally.
End-to-end verified in the paper: calc.exe launched on the operator's host, 20/20 trials, against AppAgent, AppAgentX, Mobile-Agent-v3, and MobA. The one framework that escaped this specific bug — Open-AutoGLM — passes argv as a list instead of a shell string. That's the entire fix.
The Third Lane: Unauthenticated Broadcasts
There's a quieter attack surface I want to flag because it doesn't need the perception trick at all.
Several of these frameworks talk to the phone through an app called ADB Keyboard, which listens for Android broadcasts named ADB_INPUT_B64 and ADB_INPUT_TEXT. Broadcasts of these types are unauthenticated by default. Any app on the phone can register a receiver for the same broadcast action and receive every keystroke the agent injects — with no permission prompt and no user-visible signal.
- Open-AutoGLM base64-encodes typed text and fires it at the broadcast channel. Any eavesdropper decodes it.
- Mobile-Agent-v3 allow-lists ASCII but falls back to the broadcast for anything else, character by character.
- MobA checks
text.isascii()on the whole string — one emoji in the middle and the entire message goes out over the broadcast in one shot. Passwords included.
Fake-login credential capture — an accessibility service overlays a fake sign-in Activity on a real banking app — worked 100/100 across all five agents. None hesitated. None diffed the foreground activity before typing.
Three Trust Boundaries, One String
Here's what I keep coming back to. The architecture creating this vulnerability is:
- Any Android app can render pixels the model reads.
- Any Android app can register a broadcast receiver for the debug-input channel.
- The operator's laptop runs
shell=Trueon strings derived from what the model saw.
Three trust boundaries — phone-app to agent-VLM to operator-shell — all collapsed into one string. And they're collapsed because Android doesn't have a sanctioned OS-level channel for phone-to-laptop agent control. So every framework repurposes the debug channel: adb, input text, ADB Keyboard, screencap. The debug channel was never authenticated because in its original use case, the person running adb is the person on the phone. Agents violate that assumption, but the security model didn't get rewritten to match.
This is the same shape as several other 2026 incidents:
- Anthropic's browser agent hijacked 31.5% of the time before safeguards engaged (June).
- Agentjacking through poisoned Sentry traces at 85% success against Claude Code, Cursor, and Codex (June).
- ToolPrivBench showing agents pick over-privileged tools 55–65% of the time under transient failure (June).
Every one of these is the same underlying pathology. The model treats context as instruction. The context comes from a channel that was never provenance-tagged. The tool layer runs with more privilege than the input deserved.
The Fixes, and Why They Aren't Enough
The paper's remediation section is short and useful. In descending order of effectiveness:
- Drop
shell=True. Passargvas a list. Metacharacters stay literal. Trivial one-line fix. Only Open-AutoGLM does it. - Stream screenshots over
exec-out. No device-side file, no TOCTOU. Only MobA does it. - Signature-level permissions on input broadcasts, or use explicit intents. Nobody does it.
- Diff foreground activity pre/post-action and enforce a per-task package allow-list. Defeats fake-login overlays. Nobody does it.
- Contrast-normalize screenshots before VLM ingest. The paper explicitly calls this partial, not a fix.
Note what's not on this list: any mitigation for the perception gap itself. There is no known way to teach a VLM to ignore text a human can't see without also breaking its ability to read legitimate low-contrast UI. The gap is a property of the models, not a bug in any specific one. The hardware cutout variant, similarly, has no software fix. Both are architectural facts about how phones and models work.
Sensitivity-gated confirmation prompts don't help either. Open-AutoGLM ships one. It's useless against subliminal injection, UI spoofing, and screenshot tampering — because the confirmation prompt itself is what the perception attack rewrites. And it does nothing at all against the broadcast eavesdropping lanes, because there's no "action" to confirm. The plaintext has already left.
What This Means If You Ship Agents
Three things I'd hold onto as an operator:
1. Every visual-grounding agent is re-learning XSS in slow motion. The web (mostly) solved untrusted-DOM injection by tagging where markup came from. Framebuffers have no equivalent. Every pixel the model reads is unlabeled input. Until pixel provenance exists as a primitive — impossible without OS-level cooperation — this class of attack is a permanent fixture, not a temporary bug.
2. The controller is your shell. Anywhere a model output touches subprocess, os.system, eval, or a Python f"..." interpolation into a command string is a place where a hostile pixel becomes a hostile process. shell=True is the single biggest offender. Grep your codebase. Fix it today.
3. Debug channels aren't security channels. If your agent talks to a device over adb, serial, ssh, or any other protocol that was built for human operators, you have inherited the human-operator's trust assumptions. Those assumptions do not survive automation. You need to build an authenticated transport layer or you need to enumerate every piece of the debug channel that could leak state — and there will be pieces you don't find.
The calc.exe demo is the clickable version of this story. The uncomfortable version is that every mobile-agent framework I've reviewed — including the ones I run in production — has some form of the same problem: model output crossing a boundary the input never should have been allowed to cross. The Android agents are just where the geometry is easiest to see.
A pixel doesn't have a passport. Until it does, your shell is the demilitarized zone.
Sources
- Paper: arXiv 2607.00333 — (A)I Sees What You Don't (v2, Jul 14 2026)
- Affected frameworks: AppAgent (Tencent QQGYLab), AppAgentX (Westlake AGI Lab), Mobile-Agent-v3 (Alibaba X-PLUG), Open-AutoGLM (Zhipu), MobA (OpenDFM)
Enjoyed this article?
Connect with me on LinkedIn for more insights on AI, automation, and full-stack development.
