T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/OpenClawGameBridge.js:34- Finding
Unauthenticated Bridge Permits Disclosure and Remote Control of Connected Game Clients
- Content
View full analysis
Vulnerability Details
File Location:
scripts/OpenClawGameBridge.js, lines 34–64, 174–176, 282–306, 1450–1522, and 1616–1633
Vulnerability Type: Missing authentication, authorization, origin validation, and network access restrictions
Risk Level: HighVulnerable Code
The WebSocket server is created without an explicit loopback host, authentication, or origin validation:
javascript this.wss = new WebSocket.Server({ port: this.port }); this.wss.on('connection', (ws) => { ws.isAlive = true; ws.on('pong', () => { ws.isAlive = true; }); ws.on('message', (data) => { try { const msg = JSON.parse(data); this.handleMessage(msg, ws); } catch (error) { console.error('[Bridge] Message parsing error:', error); } }); });Any connected WebSocket client can reach the command handler:
javascript case 'sendCommand': this.onBridgeSendCommand(ws, msg); break;The handler trusts the supplied player identifier and command without verifying the sender's identity or role:
javascript onBridgeSendCommand(ws, msg) { const playerUid = msg.playerUid; const command = msg.command; if (!playerUid || !command) { return; } const targetWs = this.clients.get(playerUid); if (!targetWs || targetWs.readyState !== WebSocket.OPEN) { return; } targetWs.send(JSON.stringify({ type: 'ai_control', playerUid: playerUid, command: command })); }The HTTP API is unauthenticated and enables wildcard cross-origin access:
javascript this.httpServer = http.createServer((req, res) => { if (req.method === 'OPTIONS') { res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type' }); res.end(); return; } res.setHeader ...[truncated 5658 chars]- Remediation
View remediation
Remediation Suggestions
-
Restrict all listeners to loopback
- Bind the WebSocket server explicitly to
127.0.0.1. - Make external network exposure a separate, explicit opt-in configuration.
- Add operating-system firewall restrictions as defense in depth.
- Bind the WebSocket server explicitly to
-
Require authentication
- Generate a cryptographically random secret when the bridge starts.
- Require it in an HTTP authorization header and during WebSocket connection establishment.
- Do not place reusable secrets in URLs, logs, or game chat.
- Compare credentials using a timing-safe operation.
-
Enforce role-based authorization
- Distinguish game clients from trusted controller clients.
- Permit
sendCommandonly for authenticated controller sessions. - Permit perception updates only from the game client assigned to that player.
- Bind each authenticated socket to one immutable identity.
- Reject duplicate registration instead of silently replacing an existing mapping.
-
Harden browser access
- Remove
Access-Control-Allow-Origin: *. - Disable CORS unless browser-based control is explicitly required.
- If browser access is required, use an exact origin allowlist and require authentication.
- Validate the
Originheader for WebSocket upgrades and HTTP requests. - Do not treat CORS as the sole authorization mechanism.
- Remove
-
Validate messages and commands
- Use strict schemas for every WebSocket message and HTTP request.
- Allowlist command types and their permitted fields.
- Enforce numeric coordinate ranges, string length limits, and valid NPC identifiers.
- Reject unknown fields and malformed values.
- Apply request-body size limits before buffering data.
- Add per-client rate limits to commands, registration, and perception requests.
-
Reduce exposed data
- Return only information required by the authenticated caller.
- Avoid exposing all player identifiers through public health endpoints.
- S ...[truncated 293 chars]
-
