{"id":"CVE-2026-63405","aliases":["GHSA-5p54-whvp-x327","GO-2026-6516"],"url":"https://o3.security/vulnerability/CVE-2026-63405","summary":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body","details":"### Summary\nThe Pusher-compatible REST API includes `body_md5` in the HMAC signature string but never computes or verifies the MD5 of the received HTTP body, allowing anyone who observes a signed request to replay it with an entirely different body.\n\n### Details\nIn `pusher/http.go`, the `Handler` function extracts `body_md5` from the URL query string (line 169) and includes it verbatim in `stringToSign` (line 175). It then verifies `HMAC(stringToSign, secret) == auth_signature`. After verification succeeds, `handleEvents` reads and parses `r.Body` (lines 201-212) without ever computing `md5(body)` and comparing it against the `body_md5` that was signed. The Pusher protocol specification explicitly requires the server to verify this digest to prevent body-substitution attacks. There is also no `auth_timestamp` staleness check, so replays are valid indefinitely.\n\n### PoC\n1. Capture a legitimate signed POST to `/apps/<app_id>/events?auth_key=K&auth_timestamp=T&auth_version=1.0&body_md5=LEGIT_MD5&auth_signature=SIG` carrying body `{\"name\":\"safe-event\",\"channel\":\"ch\",\"data\":\"...\"}` (e.g., from TLS-terminating load-balancer logs).\n2. Send a new request with the same query string parameters but a different body:\n   `{\"name\":\"injected-event\",\"channel\":\"admin\",\"data\":\"malicious-payload\"}`\n3. The server accepts the request (HMAC over `stringToSign` matches the original) and broadcasts the injected event to all subscribers of `admin`.\n\n### Impact\nAn attacker who can read any single signed Pusher API request (from logs, a shared proxy, or a network tap) can broadcast arbitrary events to any channel indefinitely, potentially forging server-side events, corrupting application state, or delivering phishing messages to WebSocket clients.\n\n### Fix\nAfter reading `r.Body`, compute `hex(md5(body))` and compare it to the `body_md5` query parameter using a constant-time comparison before proceeding. Additionally, reject requests whose `auth_timestamp` is more than 600 seconds from the current time.","published":"2026-09-18T16:47:37.120Z","modified":"2026-10-28T02:30:35.669764641Z","cvss":{"score":5.9,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"Go","name":"github.com/anycable/anycable","fixedVersion":"1.6.15"}],"fix":{"url":"https://github.com/anycable/anycable/commit/d2cbadec792f038f4695c84a65c0d957b0fde72c","label":"anycable/anycable@d2cbade"},"references":[{"type":"WEB","url":"https://github.com/anycable/anycable/releases/tag/v1.6.15"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/63xxx/CVE-2026-63405.json"},{"type":"ADVISORY","url":"https://github.com/anycable/anycable/security/advisories/GHSA-5p54-whvp-x327"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-63405"},{"type":"FIX","url":"https://github.com/anycable/anycable/commit/d2cbadec792f038f4695c84a65c0d957b0fde72c"},{"type":"PACKAGE","url":"https://github.com/anycable/anycable"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:35.669764641Z"}}