Skip to content

[Bug]: Feishu encryptKey bypasses config redaction, enabling webhook forgery #53412

@coygeek

Description

@coygeek

Severity Assessment

CVSS Assessment

Metric v3.1 v4.0
Score 9.1 / 10.0 8.5 / 10.0
Severity Critical High
Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:L CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:L/SC:L/SI:H/SA:L
Calculator CVSS v3.1 Calculator CVSS v4.0 Calculator

Threat Model Alignment

Classification: security-specific

The config redaction system is an explicit security boundary in OpenClaw: config.get returns a redacted snapshot specifically to prevent credential disclosure to read-scoped clients. This finding demonstrates that the Feishu encryptKey field escapes all three redaction layers (schema hints, sensitive patterns, and plugin SDK secret-input registration), leaking the sole webhook authenticator. This is not covered by the Out of Scope section — it is not a multi-tenant assumption, prompt injection, or operator-intended behavior. The encryptKey is a cryptographic secret used for webhook signature verification, and its exposure enables webhook forgery from outside the trust boundary.

Impact

When Feishu is configured in webhook mode, channels.feishu.encryptKey survives config redaction and is returned in plaintext to any operator.read client via config.get. The leaked key is the sole authenticator for inbound Feishu webhooks, enabling an attacker to forge accepted webhook events that OpenClaw processes as legitimate Feishu messages.

Affected Component

File: src/config/schema.hints.ts:104-110

const SENSITIVE_PATTERNS = [
  /token$/i,
  /password/i,
  /secret/i,
  /api.?key/i,
  /serviceaccount(?:ref)?$/i,
];

File: extensions/feishu/src/config-schema.ts:197

encryptKey: buildSecretInputSchema().optional(),

File: extensions/feishu/src/monitor.transport.ts:51-76 (signature validation)
File: extensions/feishu/src/monitor.transport.ts:217-219 (needCheck: false dispatcher invocation)
File: src/gateway/method-scopes.ts:88 (config.get in READ_SCOPE)

Technical Reproduction

  1. Configure Feishu with connectionMode: "webhook" and a plaintext channels.feishu.encryptKey value.
  2. Pair a gateway client that holds only the operator.read scope.
  3. Call config.get. Observe that the response payload at config.channels.feishu.encryptKey contains the raw key value rather than __OPENCLAW_REDACTED__.
  4. Using the leaked key, compute sha256(timestamp + nonce + encryptKey + JSON.stringify(payload)) and send a forged webhook to the Feishu path (default /feishu/events) with correct x-lark-request-timestamp, x-lark-request-nonce, and x-lark-signature headers.
  5. The request passes isFeishuWebhookSignatureValid() and is dispatched via eventDispatcher.invoke(..., { needCheck: false }).
  6. OpenClaw processes the forged event through its normal im.message.receive_v1 handler.

Demonstrated Impact

The redaction bypass is caused by three compounding gaps:

  1. Pattern miss: isSensitiveConfigPath in src/config/schema.hints.ts checks SENSITIVE_PATTERNS which match token$, password, secret, api.?key, and serviceaccount. The field name encryptKey matches none of these patterns.

  2. Missing sensitive registry: buildSecretInputSchema() in src/plugin-sdk/secret-input-schema.ts returns a plain z.union(...) without registering the schema in the sensitive Zod registry (src/config/zod-schema.sensitive.ts). The mapSensitivePaths function in schema.hints.ts checks sensitive.has(currentSchema) when walking the schema tree, but since buildSecretInputSchema never calls sensitive.add(), the encryptKey field is not flagged through this path either.

  3. Missing extension uiHints: buildChannelConfigSchema in src/channels/plugins/config-schema.ts produces only { schema: ... } with no uiHints property. The Feishu channel plugin at extensions/feishu/src/channel.ts:423 calls buildChannelConfigSchema(FeishuConfigSchema) without supplying per-field sensitivity annotations. As a result, applyChannelHints in src/config/schema.ts never adds a channels.feishu.encryptKey entry to the merged hints, and applySensitiveHints has no key to evaluate.

With the encryptKey leaked, the webhook authentication is fully compromised. isFeishuWebhookSignatureValid() in monitor.transport.ts:51-76 computes a SHA-256 HMAC using only timestamp + nonce + encryptKey + JSON.stringify(payload) and the verificationToken is not consulted in this path. The subsequent eventDispatcher.invoke(..., { needCheck: false }) at line 217-219 explicitly disables the Lark SDK's built-in request verification. The repository's own e2e test (monitor.webhook-e2e.test.ts:24-41) demonstrates that any correctly signed plaintext Feishu event envelope reaches the dispatcher.

Environment

Tested against openclaw/openclaw tag v2026.3.23-2 (commit 3b1657803292509f382fd6456242fb3e3d325461).

Prerequisites:

  • Feishu configured in webhook mode with a plaintext channels.feishu.encryptKey
  • A gateway client holding operator.read scope
  • Knowledge of any valid Feishu chat the bot participates in

Remediation Advice

Register the Zod schema returned by buildSecretInputSchema() with the sensitive registry so that mapSensitivePaths marks all fields using that schema as sensitive. Alternatively, add /encrypt.?key/i to SENSITIVE_PATTERNS as a defense-in-depth fallback. Verify that config.get returns __OPENCLAW_REDACTED__ for channels.feishu.encryptKey (and per-account encryptKey fields) when called with operator.read scope, and add regression coverage for this redaction path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions