CVE-2022-21676 (published 2022-01-12T19:15Z)

Engine.IO is the implementation of transport-based cross-browser/cross-device bi-directional communication layer for Socket.IO. A specially crafted HTTP request can trigger an uncaught exception on the Engine.IO server, thus killing the Node.js process. This impacts all the users of the `engine.io` package starting from version `4.0.0`, including those who uses depending packages like `socket.io`. Versions prior to `4.0.0` are not impacted. A fix has been released for each major branch, namely `4.1.2` for the `4.x.x` branch, `5.2.1` for the `5.x.x` branch, and `6.1.1` for the `6.x.x` branch. There is no known workaround except upgrading to a safe version.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-755
cwe_name: Improper Handling of Exceptional Conditions
description: The product does not handle or incorrectly handles an exceptional condition.
extended_description: 
            
         
         

Fix information:
repo_url: https://github.com/socketio/engine.io
commit_details: repo_url: https://github.com/socketio/engine.io
author: Damien Arrachequesne
author_date: 2022-01-11 15:34:18+01:00
msg: fix: properly handle invalid data sent by a malicious websocket client

**IMPORTANT SECURITY FIX**

A malicious client could send a specially crafted HTTP request,
triggering an uncaught exception and killing the Node.js process:

> RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
>   at Receiver.getInfo (/.../node_modules/ws/lib/receiver.js:176:14)
>   at Receiver.startLoop (/.../node_modules/ws/lib/receiver.js:136:22)
>   at Receiver._write (/.../node_modules/ws/lib/receiver.js:83:10)
>   at writeOrBuffer (internal/streams/writable.js:358:12)

This bug was introduced by [1], included in `engine.io@4.0.0`, so
previous releases are not impacted.

[1]: https://github.com/socketio/engine.io/commit/f3c291fa613a9d50c924d74293035737fdace4f2

Thanks to Marcus Wejderot from Mevisio for the responsible disclosure.
repository: repo_url: https://github.com/socketio/engine.io
repo_name: socketio/engine.io
description: The engine used in the Socket.IO JavaScript server, which manages the low-level transports such as HTTP long-polling and WebSocket. 
owner: socketio
file_changes: filename: server.ts
change_type: ModificationType.MODIFY
diff: @@ -609,9 +609,6 @@ export class Server extends BaseServer {
         client.maybeUpgrade(transport);
       }
     } else {
-      // transport error handling takes over
-      websocket.removeListener("error", onUpgradeError);
-
       const closeConnection = (errorCode, errorContext) =>
         abortUpgrade(socket, errorCode, errorContext);
       this.handshake(req._query.transport, req, closeConnection);

programming_language: TypeScript
filename: server.js
change_type: ModificationType.MODIFY
diff: @@ -157,6 +157,46 @@ describe("server", () => {
         }
       );
     });
+
+    it("should not throw when the client sends invalid data during the handshake (ws only)", done => {
+      listen(port => {
+        // will throw "RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear"
+        request
+          .get(`http://localhost:${port}/engine.io/`)
+          .set("connection", "upgrade")
+          .set("upgrade", "websocket")
+          .set("Sec-WebSocket-Version", "13")
+          .set("Sec-WebSocket-Key", "DXR4dX615eRds8nRmlhqtw==")
+          .query({ transport: "websocket", EIO: 4 })
+          .send("test")
+          .end(() => {});
+
+        setTimeout(done, 50);
+      });
+    });
+
+    it("should not throw when the client sends invalid data during the handshake (upgrade)", done => {
+      listen(port => {
+        request
+          .get(`http://localhost:${port}/engine.io/`)
+          .query({ transport: "polling", EIO: 4 })
+          .end((err, res) => {
+            const sid = JSON.parse(res.text.substring(1)).sid;
+
+            request
+              .get(`http://localhost:${port}/engine.io/`)
+              .set("connection", "upgrade")
+              .set("upgrade", "websocket")
+              .set("Sec-WebSocket-Version", "13")
+              .set("Sec-WebSocket-Key", "DXR4dX615eRds8nRmlhqtw==")
+              .query({ transport: "websocket", EIO: 4, sid })
+              .send("test")
+              .end(() => {});
+
+            setTimeout(done, 50);
+          });
+      });
+    });
   });
 
   describe("handshake", () => {

programming_language: TypeScript