Submit #1005378: https://github.com/777genius agent-teams-ai 2.15.0 Local Sensitive Logic Leading to Sensitive Information Leakageinfo

Titlehttps://github.com/777genius agent-teams-ai 2.15.0 Local Sensitive Logic Leading to Sensitive Information Leakage
Description# Agent Teams AI Local Fastify Sidecar Unauthenticated Sensitive Information Disclosure **Affected product**: Agent Teams AI desktop application (Electron), verified on version 2.1.2 (Windows x64) **Vulnerability type**: Missing authentication / local sensitive information disclosure (CWE-306, CWE-200) **Threat scenario**: Any process on the same machine, including browser extensions and local contexts reachable from a web page ## 1. Vulnerability Description While Agent Teams AI is running, it starts a Fastify-based local HTTP service (a sidecar) that serves configuration, project, session, search, team, SSH, notification, and token-usage data to the renderer process. The service binds to `127.0.0.1` and picks a port starting at `3456` (incrementing up to `3466` on collision). It runs under two conditions: when `httpServer.enabled` is set to `true` in the configuration, the server starts together with the application; when team control functionality needs a control URL, the server is started on demand. Users are generally unaware that this service exists. The problem is that **none of the `/api/*` routes have any authentication, session validation, or request-token verification**. The only line of defense is a CORS origin check, but CORS only restricts read access for "cross-origin pages inside a browser." It has no effect on non-browser local processes: browser extensions (which can bypass the same-origin policy and read responses once host permissions are granted), any local program running with low privileges, or even a malicious script on the same machine can send ordinary HTTP GET requests to `127.0.0.1:3456-3466` and read the full responses without any credential. Without any user interaction, an attacker can silently read: - The full application configuration: the Claude root directory, custom project paths, SSH profiles (host, username, port, identity-file path), the most recent SSH connection, provider endpoint settings, and notification/runtime settings; - Project inventories and session data: project paths, session IDs/titles/message counts, and **complete session message contents** (including user prompts, assistant replies, tool calls with their parameters, and subagent records); - Global session search results, including matched text and surrounding context; - Team configurations, members, runtime state, and diagnostics (process PIDs, command lines, working directories); - Notification history, token usage and cost data, and organization information. This amounts to broad local privacy and intellectual-property disclosure. Although the API does not directly return provider API keys, SSH passwords, or browser cookies, the exposed SSH host/username/identity-file paths and project paths provide precise intelligence for follow-up targeted attacks such as private-key theft or tailored phishing. ## 2. Vulnerability Analysis ### 2.1 The server has no authentication layer at all In `src/main/services/infrastructure/HttpServer.ts`, the `doStart` method creates the Fastify instance, registers only the CORS plugin, and then registers all API routes. No authentication hook of any kind is mounted in between: there is no `onRequest` verification, no bearer-token check, and no session validation: ```ts this.app = Fastify({ logger: false }); const corsOrigin = process.env.CORS_ORIGIN; if (corsOrigin === '*') { await this.app.register(cors, { origin: true, credentials: true }); } else if (corsOrigin) { const origins = corsOrigin.split(',').map((o) => o.trim()); await this.app.register(cors, { origin: origins, credentials: true }); } else { const localhostPattern = /^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?$/; await this.app.register(cors, { origin: (origin, cb) => { if (!origin) { cb(null, true); // <- requests without an Origin header pass directly return; } if (localhostPattern.test(origin)) { cb(null, true); return; } cb(new Error('Not allowed by CORS'), false); }, credentials: true, }); } ``` There is a critical misunderstanding here: **CORS is not an authentication mechanism**. The `origin` callback is only exercised for browser cross-origin requests. Requests issued by local Python scripts, `curl`, or any other local process carry no `Origin` header at all, and hit `if (!origin) { cb(null, true); }`, which allows them through immediately. In other words, the CORS check only blocks one scenario - "a public web page using a browser to read the response cross-origin" - while providing zero protection against the most relevant attacker in this threat model: a local process such as a browser extension or local malware. ### 2.2 The port range is fixed and predictable ```ts preferredPort: number = 3456, ... for (let attempt = 0; attempt <= 10; attempt++) { const tryPort = preferredPort + attempt; try { await this.app.listen({ host, port: tryPort }); this.port = tryPort; ... ``` The server always tries ports `3456` through `3466`. An attacker only needs to probe these 11 ports to locate the service; no full port scan is required. ### 2.3 Sensitive routes return internal data directly Every route handler reads internal services and returns the complete data without any notion of caller identity. Taking `src/main/http/config.ts` as an example: ```ts app.get('/api/config', async () => { const config = configManager.getConfig(); return { success: true, data: config }; }); ``` `getConfig()` returns the **entire application configuration object** held by `ConfigManager`, including `ssh.profiles` (SSH hosts, usernames, ports, auth methods, identity-file paths), `ssh.lastConnection`, `providerConnections` (per-provider endpoint settings), and `general.customProjectPaths`. All of it is returned verbatim to the caller. Session routes (`src/main/http/sessions.ts`) behave the same way: ```ts app.get<{ Params: { projectId: string } }>( '/api/projects/:projectId/sessions', async (request) => { const validated = validateProjectId(request.params.projectId); if (!validated.valid) return []; const sessions = await services.projectScanner.listSessions(validated.value!); return sessions; } ); ``` `GET /api/projects/:id/sessions/:id` goes further and returns the fully parsed session content: user messages, assistant replies, tool-call inputs and outputs, subagent records, and metrics. Sessions contain the complete history of user-AI interaction plus project code context, which is highly sensitive. ### 2.4 Attack chain summary 1. The user starts Agent Teams AI normally. The sidecar listens on `127.0.0.1:3456` (or an incremented port) once enabled by configuration or triggered by team functionality; 2. A local attacker (browser extension or local process) sends an HTTP GET to `127.0.0.1:3456` without any credential; 3. The request has no `Origin` header, so the CORS callback passes it through; the route handler executes and returns the full JSON; 4. The attacker reads the response and completes the exfiltration. No alert is logged and the user notices nothing. ## 3. Proof of Concept ### Reproduction steps 1. Start the Agent Teams AI desktop application and make sure the sidecar is running (either `httpServer.enabled` is `true` in the configuration, or team control functionality has been triggered so the server started on demand); 2. Save the script below as `poc.py` and run `python poc.py` in any environment with Python 3.10+ (optional arguments are documented via `--help`; for example `--query "keyword"` tests global search, and `--project-id <id> --session-id <id>` reads a complete session); 3. The script first discovers the service on ports `3456`-`3466` via `/api/version`, then requests each sensitive endpoint and prints the returned content. In testing, every endpoint returned `HTTP 200` with full JSON data, and no token or credential was ever sent. Sample output (excerpt): ```text [+] Found Fastify sidecar: http://127.0.0.1:3456 (version 2.1.2) === full config: /api/config [HTTP 200] === { "success": true, "data": { "general": { "customProjectPaths": ["E:\\work\\secret-project"], ... }, "ssh": { "profiles": [{ "host": "10.0.0.5", "user": "deploy", ... }], "lastConnection": { ... } }, "providerConnections": { ... } } } === ssh last connection: /api/ssh/last-connection [HTTP 200] === { "success": true, "data": { "host": "10.0.0.5", "port": 22, "user": "deploy", "authMethod": "key", "identityFile": "C:\\Users\\xx\\.ssh\\id_ed25519" } } === teams: /api/teams [HTTP 200] === ... ```
Source⚠️ https://drive.google.com/drive/folders/1Fhsrm4JeR8TAT0rGNdGbGzVh9LNyoDG5?usp=sharing
User
 PoriaMs (UID 101188)
Submission09/29/2026 16:22 (1 month ago)
Moderation10/25/2026 15:38 (26 days later)
StatusAccepted
VulDB entry426277 [777genius agent-teams-ai up to 2.17.10 Local HTTP Service HttpServer.ts doStart missing authentication]
Points20

Do you want to use VulDB in your project?

Use the official API to access entries easily!