T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- index.ts:199
- Finding
- Inbound DingTalk callbacks are accepted without authentication or access-policy enforcement<![CDATA[ ## Vulnerability Details **File Location**: `index.ts:199-218` **Vulnerability Type**: Missing callback authentication and authorization **Risk Level**: High ### Vulnerable Code ```typescript async receive( ctx: ChannelPluginContext<DingTalkConfig>, payload: unknown ): Promise<InboundMessage | null> { // Handle incoming webhook from DingTalk const data = payload as Record<string, unknown>; // DingTalk callback format if (data.msgtype === "text" && data.text) { const textData = data.text as { content?: string }; return { id: String(data.msgId || Date.now()), channel: "dingtalk", content: { type: "text", text: textData.content || "" }, authorId: String(data.senderStaffId || data.staffId || "unknown"), authorName: String(data.senderNick || "Unknown"), conversationId: String(data.conversationId || data.chatId || "private"), timestamp: new Date(Number(data.createTime) || Date.now()), }; } return null; } ``` ### Technical Analysis The callback handler casts an untrusted payload to a record and creates an `InboundMessage` based solely on attacker-controlled fields. It does not verify a DingTalk callback signature, timestamp, nonce, encryption wrapper, or replay status. The configuration declares `encryptKey`, `dmPolicy`, `allowFrom`, `groupPolicy`, and `groupAllowFrom`, but this handler does not use any of them. Consequently, a payload can claim an arbitrary `senderStaffId`, `staffId`, `conversationId`, or `chatId`, and the configured allowlists do not provide effective protection within the audited implementation. The repository includes a `DingTalkCallbackPayload` type containing `encrypt`, `msg_signature`, `timestamp`, and `nonce`, but the callback implementation does not process those security fields. ### Attack Path 1. An attacker identifies or gains network access to the OpenClaw DingTalk callback route. 2. The attacker submits a forged payload such as a text message with an ...[truncated 1021 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Validate every inbound callback using DingTalk's documented signature-verification procedure before parsing message content. 2. Verify the timestamp and nonce and reject stale callbacks. 3. Maintain a bounded replay cache for recently accepted message IDs, signatures, or nonces. 4. Use constant-time comparison for authentication values. 5. Decrypt encrypted callback envelopes using the configured encryption key where the DingTalk integration requires encryption. 6. Reject unsigned or malformed payloads by default rather than falling back to permissive parsing. 7. Determine whether the event is a direct or group message and enforce: - `dmPolicy` and `allowFrom` for direct messages. - `groupPolicy` and `groupAllowFrom` for group messages. 8. Do not trust sender or conversation identifiers until callback authenticity has been established. 9. Add tests for forged signatures, stale timestamps, replayed messages, spoofed sender IDs, and allowlist bypass attempts. ]]>
