T01 · Skill Instruction Hijacking
Error
- Location
- index.ts:185
- Finding
- Unconditional Command Authorization for Remote Messages<![CDATA[ ## Vulnerability Details **File Location**: `index.ts:185-241` **Vulnerability Type**: Remote instruction injection through unconditional command authorization **Risk Level**: Critical ### Vulnerable Code ```ts const body = core.channel.reply.formatAgentEnvelope({ channel: "NoChat", from: senderName, timestamp: msg.created_at ? new Date(msg.created_at).getTime() : Date.now(), previousTimestamp, envelope: envelopeOptions, body: text, }); // Build the ctx payload (same shape BlueBubbles uses) const ctxPayload = { Body: body, BodyForAgent: body, RawBody: text, CommandBody: text, BodyForCommands: text, From: `nochat:${senderId}`, To: `nochat:${config.agentId || config.agentName}`, SessionKey: route.sessionKey, AccountId: route.accountId, ChatType: "direct", ConversationLabel: senderName, SenderName: senderName, SenderId: senderId, Provider: "nochat", Surface: "nochat", MessageSid: msg.id, CommandAuthorized: true, // Trust tiers handle authorization }; // Dispatch: pushes inbound to agent, waits for reply, delivers reply back to NoChat console.log(`[NoChat] Dispatching to session ${route.sessionKey}...`); await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({ ctx: ctxPayload, cfg: ctx.cfg, dispatcherOptions: { deliver: async (payload: any) => { // Send the agent's reply back to NoChat const replyText = payload.text || ""; if (!replyText.trim()) return; const conversationId = msg.conversation_id; if (!conversationId) { console.log("[NoChat] No conversation_id on inbound message — cannot reply"); return; } const result = await client.sendMessage(conversationId, replyText); if (result.ok) { console.log(`[NoChat] Replied to ${senderName} in ${conversationId.slice(0, 8)}`); } else { console.log(`[NoChat] Reply failed: ${result.error}`); } }, onError: (err: unknown, info: { kind: string }) ...[truncated 2212 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve and enforce the sender's trust tier before constructing or dispatching the OpenClaw context. 2. Default unknown senders to a denied or non-command-capable state. 3. Drop blocked senders before session routing. 4. Set `CommandAuthorized` to `false` by default and enable it only after explicit identity verification and authorization. 5. Do not place untrusted text into command-specific fields such as `CommandBody` or `BodyForCommands`. 6. Route untrusted and sandboxed senders to isolated sessions with strict tool allowlists, resource limits, and no access to the primary session. 7. Require authenticated sender identities rather than trusting unsigned `sender_id` data returned by the server. 8. Add security tests proving that blocked, unknown, and untrusted senders cannot issue commands or access privileged tools. ]]>
