T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/index.js:248
- Finding
- Dashboard Binds to All Network Interfaces Without Authentication by Default<![CDATA[ ## Vulnerability Details **File Location**: `src/config.js:207-211`, `src/config.js:235-238`, `src/index.js:248-269`, `src/index.js:291-621`, `src/index.js:630-632` **Vulnerability Type**: Missing authentication and incorrect network binding **Risk Level**: High ### Vulnerable Code ```js // src/config.js server: { port: parseInt(process.env.PORT || fileConfig.server?.port || "3333", 10), host: process.env.HOST || fileConfig.server?.host || "localhost", }, ``` ```js // src/config.js auth: { mode: process.env.DASHBOARD_AUTH_MODE || fileConfig.auth?.mode || "none", token: process.env.DASHBOARD_TOKEN || fileConfig.auth?.token, // ... }, ``` ```js // src/index.js const server = http.createServer((req, res) => { // CORS headers res.setHeader("Access-Control-Allow-Origin", "*"); const urlParts = req.url.split("?"); const pathname = urlParts[0]; const query = new URLSearchParams(urlParts[1] || ""); // Fast path for health check if (pathname === "/api/health") { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ status: "ok", port: PORT, timestamp: new Date().toISOString() })); return; } // Auth check (unless public path) const isPublicPath = AUTH_CONFIG.publicPaths.some( (p) => pathname === p || pathname.startsWith(p + "/"), ); if (!isPublicPath && AUTH_CONFIG.mode !== "none") { const authResult = checkAuth(req, AUTH_CONFIG); ``` ```js // src/index.js server.listen(PORT, () => { const profile = process.env.OPENCLAW_PROFILE; console.log(`🦞 OpenClaw Command Center running at http://localhost:${PORT}`); ``` ### Technical Analysis The configuration declares a default host of `localhost`, but that value is never passed to `server.listen()`. Calling `server.listen(PORT)` without a host causes Node.js to listen on the unspecified address, generally `::` or `0.0.0.0`, rather than restricting the service to the loopback interface. At the same time, authentication d ...[truncated 2597 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce the configured host when starting the server: ```js server.listen(PORT, CONFIG.server.host, () => { // ... }); ``` 2. Default to an explicit loopback address such as `127.0.0.1`, rather than relying on hostname resolution. 3. Refuse to start when authentication is disabled and the configured address is not loopback. 4. Require authentication for all sensitive APIs and require separate authorization for mutation and job-control routes. 5. Change `/api/action` to accept only authenticated `POST` requests. 6. Consider disabling job-control endpoints unless explicitly enabled. 7. Add automated tests that verify the default process is inaccessible through non-loopback interfaces. 8. Update startup logging so that it reports the address actually bound rather than always printing `localhost`. ]]>
