{"id":"CVE-2026-63405","aliases":["EUVD-2026-83180","GHSA-5p54-whvp-x327","GO-2026-6516"],"status":"published","title":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body","description":"AnyCable is a realtime server for reliable two-way communication that supports any backend. Prior to 1.6.15, the Pusher-compatible REST API in pusher/http.go includes the caller-supplied body_md5 value in the HMAC input but does not calculate the digest of the received request body or compare it with the signed value. An attacker who obtains a legitimate signed POST request can retain its query parameters and auth_signature while replacing the body, causing Handler and handleEvents to accept and broadcast attacker-selected event content. The absence of an auth_timestamp freshness check also allows the captured signature to be replayed indefinitely. This can forge server-side events, modify application state, or deliver attacker-controlled messages to WebSocket clients within the signed request's application context. This issue is fixed in version 1.6.15.","severity":{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00209,"percentile":0.11066,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-345"],"published":"2026-09-18T16:47:37Z","modified":"2026-10-28T02:30:35Z","fixes":[{"ecosystem":"Go","package":"github.com/anycable/anycable","fixed_in":["1.6.15"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"github.com/anycable/anycable","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.6.14"},{"fixed":"1.6.15"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/anycable/anycable","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.6.15"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/anycable/anycable-go","ranges":[{"type":"SEMVER","events":[{"introduced":"0"}]}]}],"products":[{"vendor":"anycable","product":"anycable"}],"references":[{"url":"https://github.com/anycable/anycable/security/advisories/GHSA-5p54-whvp-x327","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/anycable/anycable/commit/d2cbadec792f038f4695c84a65c0d957b0fde72c","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/anycable/anycable/releases/tag/v1.6.15","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/advisories/GHSA-5p54-whvp-x327"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/63xxx/CVE-2026-63405.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-63405","tags":["advisory"]},{"url":"https://github.com/anycable/anycable","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-63405","title":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body","description":"AnyCable is a realtime server for reliable two-way communication that supports any backend. Prior to 1.6.15, the Pusher-compatible REST API in pusher/http.go includes the caller-supplied body_md5 value in the HMAC input but does not calculate the digest of the received request body or compare it with the signed value. An attacker who obtains a legitimate signed POST request can retain its query parameters and auth_signature while replacing the body, causing Handler and handleEvents to accept and broadcast attacker-selected event content. The absence of an auth_timestamp freshness check also allows the captured signature to be replayed indefinitely. This can forge server-side events, modify application state, or deliver attacker-controlled messages to WebSocket clients within the signed request's application context. This issue is fixed in version 1.6.15.","status":"published","published":"2026-09-18T16:47:37.12Z","modified":"2026-09-18T17:07:38.72Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:19:55.914391Z"}],"euvd":[{"id":"EUVD-2026-83180","description":"AnyCable is a realtime server for reliable two-way communication that supports any backend. Prior to 1.6.15, the Pusher-compatible REST API in pusher/http.go includes the caller-supplied body_md5 value in the HMAC input but does not calculate the digest of the received request body or compare it with the signed value. An attacker who obtains a legitimate signed POST request can retain its query parameters and auth_signature while replacing the body, causing Handler and handleEvents to accept and broadcast attacker-selected event content. The absence of an auth_timestamp freshness check also allows the captured signature to be replayed indefinitely. This can forge server-side events, modify application state, or deliver attacker-controlled messages to WebSocket clients within the signed request's application context. This issue is fixed in version 1.6.15.","status":"published","published":"2026-09-18T16:47:37Z","modified":"2026-09-18T17:07:38Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-02T17:28:08.412343Z"}],"ghsa":[{"id":"GHSA-5p54-whvp-x327","title":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body","description":"### 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/\u003capp_id\u003e/events?auth_key=K\u0026auth_timestamp=T\u0026auth_version=1.0\u0026body_md5=LEGIT_MD5\u0026auth_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.","status":"published","published":"2026-09-18T17:16:25Z","modified":"2026-09-18T17:16:28Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:41:19.455935Z"}],"nvd":[{"id":"CVE-2026-63405","description":"AnyCable is a realtime server for reliable two-way communication that supports any backend. Prior to 1.6.15, the Pusher-compatible REST API in pusher/http.go includes the caller-supplied body_md5 value in the HMAC input but does not calculate the digest of the received request body or compare it with the signed value. An attacker who obtains a legitimate signed POST request can retain its query parameters and auth_signature while replacing the body, causing Handler and handleEvents to accept and broadcast attacker-selected event content. The absence of an auth_timestamp freshness check also allows the captured signature to be replayed indefinitely. This can forge server-side events, modify application state, or deliver attacker-controlled messages to WebSocket clients within the signed request's application context. This issue is fixed in version 1.6.15.","status":"published","published":"2026-09-18T17:16:59.463Z","modified":"2026-09-24T21:25:27.05Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T14:12:50.043898Z"}],"osv":[{"id":"CVE-2026-63405","title":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body","description":"AnyCable is a realtime server for reliable two-way communication that supports any backend. Prior to 1.6.15, the Pusher-compatible REST API in pusher/http.go includes the caller-supplied body_md5 value in the HMAC input but does not calculate the digest of the received request body or compare it with the signed value. An attacker who obtains a legitimate signed POST request can retain its query parameters and auth_signature while replacing the body, causing Handler and handleEvents to accept and broadcast attacker-selected event content. The absence of an auth_timestamp freshness check also allows the captured signature to be replayed indefinitely. This can forge server-side events, modify application state, or deliver attacker-controlled messages to WebSocket clients within the signed request's application context. This issue is fixed in version 1.6.15.","status":"published","published":"2026-09-18T16:47:37.12Z","modified":"2026-10-28T02:30:35.669764641Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T15:45:13.306117Z"},{"id":"GHSA-5p54-whvp-x327","title":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body","description":"### 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/\u003capp_id\u003e/events?auth_key=K\u0026auth_timestamp=T\u0026auth_version=1.0\u0026body_md5=LEGIT_MD5\u0026auth_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.","status":"published","published":"2026-09-18T17:16:25Z","modified":"2026-09-28T17:10:52.635822676Z","cvss":[{"version":"3.1","score":5.9,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-345"],"updated_at":"2026-11-02T15:46:38.553953Z"},{"id":"GO-2026-6516","title":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body in github.com/anycable/anycable-go","description":"AnyCable: Pusher REST API Does Not Verify Request Body MD5 Enabling Signed-Request Replay with Arbitrary Body in github.com/anycable/anycable-go.\n\nNOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions.\n\n(If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.)\n\nThe additional affected modules and versions are: github.com/anycable/anycable-go before v1.6.15.","status":"published","published":"2026-09-28T16:43:40Z","modified":"2026-09-28T17:00:16.36126972Z","updated_at":"2026-11-02T15:47:03.11707Z"}]}}
