High severity7.3NVD Advisory· Published Apr 23, 2026· Updated Apr 29, 2026
CVE-2026-41342
CVE-2026-41342
Description
OpenClaw before 2026.3.28 contains an authentication bypass vulnerability in the remote onboarding component that persists unauthenticated discovery endpoints without explicit trust confirmation. Attackers can spoof discovery endpoints to redirect onboarding toward malicious gateways and capture gateway credentials or traffic.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.28 | 2026.3.28 |
Affected products
1Patches
1d6affb17d85fCLI: confirm discovered remote gateways before saving config (#55895)
2 files changed · +122 −0
src/commands/onboard-remote.test.ts+104 −0 modified@@ -83,6 +83,7 @@ describe("promptRemoteGatewayConfig", () => { displayName: "Gateway", host: "gateway.tailnet.ts.net", port: 18789, + gatewayTlsFingerprintSha256: "sha256:abc123", }, ]); @@ -111,12 +112,115 @@ describe("promptRemoteGatewayConfig", () => { expect(next.gateway?.mode).toBe("remote"); expect(next.gateway?.remote?.url).toBe("wss://gateway.tailnet.ts.net:18789"); expect(next.gateway?.remote?.token).toBe("token-123"); + expect(next.gateway?.remote?.tlsFingerprint).toBe("sha256:abc123"); expect(prompter.note).toHaveBeenCalledWith( expect.stringContaining("Direct remote access defaults to TLS."), "Direct remote", ); }); + it("rejects discovery endpoint when trust confirmation is declined", async () => { + detectBinary.mockResolvedValue(true); + discoverGatewayBeacons.mockResolvedValue([ + { + instanceName: "evil", + displayName: "Evil", + host: "evil.example", + port: 443, + gatewayTlsFingerprintSha256: "sha256:attacker", + }, + ]); + + const select = createSelectPrompter({ + "Select gateway": "0", + "Connection method": "direct", + }); + const confirm: WizardPrompter["confirm"] = vi.fn(async (params) => { + if (params.message.startsWith("Discover gateway")) { + return true; + } + if (params.message.startsWith("Trust this gateway")) { + return false; + } + return false; + }); + + const prompter = createPrompter({ + confirm, + select, + text: vi.fn(async () => "") as WizardPrompter["text"], + }); + + await expect(promptRemoteGatewayConfig({} as OpenClawConfig, prompter)).rejects.toThrow( + "not trusted", + ); + }); + + it("trusts discovery endpoint without fingerprint and omits tlsFingerprint", async () => { + detectBinary.mockResolvedValue(true); + discoverGatewayBeacons.mockResolvedValue([ + { + instanceName: "gw", + displayName: "Gateway", + host: "gw.example", + port: 18789, + }, + ]); + + const text: WizardPrompter["text"] = vi.fn(async (params) => { + if (params.message === "Gateway WebSocket URL") { + return String(params.initialValue); + } + return ""; + }) as WizardPrompter["text"]; + + const { next } = await runRemotePrompt({ + text, + confirm: true, + selectResponses: { + "Select gateway": "0", + "Connection method": "direct", + "Gateway auth": "off", + }, + }); + + expect(next.gateway?.remote?.url).toBe("wss://gw.example:18789"); + expect(next.gateway?.remote?.tlsFingerprint).toBeUndefined(); + }); + + it("drops discovery tlsFingerprint when the URL is edited after trust confirmation", async () => { + detectBinary.mockResolvedValue(true); + discoverGatewayBeacons.mockResolvedValue([ + { + instanceName: "gateway", + displayName: "Gateway", + host: "gateway.tailnet.ts.net", + port: 18789, + gatewayTlsFingerprintSha256: "sha256:abc123", + }, + ]); + + const text: WizardPrompter["text"] = vi.fn(async (params) => { + if (params.message === "Gateway WebSocket URL") { + return "wss://other.example:443"; + } + return ""; + }) as WizardPrompter["text"]; + + const { next } = await runRemotePrompt({ + text, + confirm: true, + selectResponses: { + "Select gateway": "0", + "Connection method": "direct", + "Gateway auth": "off", + }, + }); + + expect(next.gateway?.remote?.url).toBe("wss://other.example:443"); + expect(next.gateway?.remote?.tlsFingerprint).toBeUndefined(); + }); + it("does not route from TXT-only discovery metadata", async () => { detectBinary.mockResolvedValue(true); discoverGatewayBeacons.mockResolvedValue([
src/commands/onboard-remote.ts+18 −0 modified@@ -52,6 +52,8 @@ export async function promptRemoteGatewayConfig( ): Promise<OpenClawConfig> { let selectedBeacon: GatewayBonjourBeacon | null = null; let suggestedUrl = cfg.gateway?.remote?.url ?? DEFAULT_GATEWAY_URL; + let discoveryTlsFingerprint: string | undefined; + let trustedDiscoveryUrl: string | undefined; const hasBonjourTool = (await detectBinary("dns-sd")) || (await detectBinary("avahi-browse")); const wantsDiscover = hasBonjourTool @@ -113,10 +115,23 @@ export async function promptRemoteGatewayConfig( }); if (mode === "direct") { suggestedUrl = `wss://${host}:${port}`; + const fingerprint = target.endpoint.gatewayTlsFingerprintSha256; + const trusted = await prompter.confirm({ + message: `Trust this gateway? Host: ${host}:${port} TLS fingerprint: ${fingerprint ?? "not advertised (connection will not be pinned)"}`, + initialValue: false, + }); + if (!trusted) { + throw new Error( + `Discovery endpoint ${host}:${port} not trusted. Re-run onboarding or enter the URL manually.`, + ); + } + discoveryTlsFingerprint = fingerprint; + trustedDiscoveryUrl = suggestedUrl; await prompter.note( [ "Direct remote access defaults to TLS.", `Using: ${suggestedUrl}`, + ...(fingerprint ? [`TLS pin: ${fingerprint}`] : []), "If your gateway is loopback-only, choose SSH tunnel and keep ws://127.0.0.1:18789.", ].join("\n"), "Direct remote", @@ -141,6 +156,8 @@ export async function promptRemoteGatewayConfig( validate: (value) => validateGatewayWebSocketUrl(String(value)), }); const url = ensureWsUrl(String(urlInput)); + const pinnedDiscoveryFingerprint = + discoveryTlsFingerprint && url === trustedDiscoveryUrl ? discoveryTlsFingerprint : undefined; const authChoice = await prompter.select({ message: "Gateway auth", @@ -231,6 +248,7 @@ export async function promptRemoteGatewayConfig( url, ...(token !== undefined ? { token } : {}), ...(password !== undefined ? { password } : {}), + ...(pinnedDiscoveryFingerprint ? { tlsFingerprint: pinnedDiscoveryFingerprint } : {}), }, }, };
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
5- github.com/advisories/GHSA-3cw3-5vxw-g2h3ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-3cw3-5vxw-g2h3nvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-41342ghsaADVISORY
- www.vulncheck.com/advisories/openclaw-unauthenticated-discovery-endpoint-credential-exfiltration-via-remote-onboardingnvdThird Party AdvisoryWEB
- github.com/openclaw/openclaw/commit/d6affb17d85f5f5ab08ef9f2b994b257af12e75aghsaWEB
News mentions
0No linked articles in our index yet.