# Signal K Server vulnerable to JWT Token Theft via WebSocket Enumeration and Unauthenticated Polling

Signal K Server leaks ACCESS_REQUEST events to unauthenticated WebSocket clients and exposes JWT tokens via unauthenticated polling of /access/requests/:id, enabling full auth bypass.

- Payout: Research
- Severity: critical
- Program: Signal K Server
- Found by: atsc11
- Disclosed: 2026-01-02
- CVE: CVE-2025-68620
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-fq56-hvg6-wvm5
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-fq56-hvg6-wvm5
- Tags: area:web domain:api domain:web impact:ato impact:privesc kind:research platform:ghsa sev:critical tech:jwt tech:websocket technique:source-review vuln:access-control vuln:auth-bypass vuln:info-disclosure

**Target:** Signal K Server WebSocket stream endpoint and REST access request endpoint
**Root cause:** The `startServerEvents` function broadcasts all cached server events (including ACCESS_REQUEST) to any WebSocket client without authorization checks, and the `/signalk/v1/access/requests/:id` endpoint uses readonly auth and returns the full request object including the issued JWT token.
**Steps:**
1. Connect to `ws://TARGET/signalk/v1/stream?serverevents=all` without authentication.
2. Receive cached `ACCESS_REQUEST` events containing requestId, clientRequest.permissions, and href.
3. Poll `GET http://TARGET{href}` (e.g. `/signalk/v1/requests/{requestId}`) unauthenticated until state is `COMPLETED`.
4. Read `accessRequest.token` from the JSON response to obtain the JWT.
**How found:** Code review of `src/events.ts` startServerEvents (no auth check on cached event broadcast), `src/tokensecurity.js` getAccessRequestsResponse (returns full pending requests), and `src/requestResponse.js` createReply (includes token field on approval), combined with the readonly auth on the REST endpoint.
**Exploitation:** Attacker either creates their own access request (optionally with spoofed IP) and polls their own request ID, or passively monitors the WebSocket stream for legitimate device request IDs and polls those. When an admin approves, the polling response returns the plaintext JWT, granting the attacker the approved permission level (potentially admin) or hijacking legitimate device credentials.
**Escalation:** Requesting admin permissions in the crafted access request yields a fully privileged admin token, granting access to all protected endpoints including package installation.
**Insight:** When a server caches events for replay to new WebSocket clients, audit whether sensitive event types (access requests, tokens, credentials) are filtered by the connecting client's auth level — replay caches often bypass per-event authorization. Also check whether status/polling endpoints for async approval flows return the issued credential in the response body.

