Listmonkの2FAバイパス、修正は7.0ベータのみ(CVE-2026-128332)
listmonk 6.0.0から6.2.0は、パスワードを持つ攻撃者がTOTPの2FAを回避できる状態です。修正版は7.0.0-beta.2だけです。
要点
- listmonkの2つの不具合がTOTPによる二要素認証を無効化します。1つはパスワードリセット後にコードを省略し、もう1つはコードの推測回数を制限しません。VulnCheckは2026年11月13日にCVE-2026-128333とCVE-2026-128332を公開しました。
- listmonk 6.0.0から6.2.0、および7.0.0-beta.1が影響を受けます。どちらの不具合も、有効なパスワードまたは完了したリセットと、TOTPが有効なアカウントを必要とします。
- スコアはCVSS v4.0 9.1 Critical、CVSS v3.1 7.4 Highです。悪用は確認されておらず、2026年11月13日時点でどちらのCVEもCISAのKEVカタログにありません。
- 両方の修正を含む唯一のリリースである7.0.0-beta.2へ更新してください。6.x系にパッチはありません。ベータへ移行できない場合は、管理ログインのエンドポイントにレート制限をかけてください。「修正方法」を参照してください。
影響を受けますか?
次の条件がすべて当てはまる場合に影響を受けます。
- listmonk 6.0.0以降、6.2.0まで、または7.0.0-beta.1を実行している。
- 少なくとも1つのアカウント、とくに管理者アカウントでTOTPの二要素認証が有効になっている。
- CVE-2026-128332では、攻撃者がそのアカウントのパスワードを知っている。CVE-2026-128333では、攻撃者がそのアカウントのパスワードリセットを完了できる。たとえば、そのメールを読める場合です。
- 攻撃者から
/adminインターフェースへ到達できる。
listmonk 5.x以前はアドバイザリの影響範囲外です。TOTPのないアカウントには回避できる第二要素がないため、リセットの不具合ではパスワードだけが障壁になります。総当たりの不具合には、推測する6桁のコードと、開始するためのパスワードが必要です。
修正版があるのは2つの系列のうち1つだけです。7.0ベータは修正済みで、6.x系にバックポートはありません。
| 系列 | 影響を受けるバージョン | 修正バージョン |
|---|---|---|
| 6.x | 6.0.0から6.2.0 | なし。修正は7.0.0-beta.2のみ |
| 7.0ベータ | 7.0.0-beta.1 | 7.0.0-beta.2(2026年11月10日) |
実行中のバージョンを確認します。バイナリと公式Dockerイメージはどちらも--versionに応答します。
# Binary install
/path/to/listmonk --version
# Docker
docker exec <container> /listmonk/listmonk --version
2FAが有効なアカウントを一覧表示します。listmonkのデータベースに対して次を実行します。
docker exec -i <postgres-container> psql -U listmonk -d listmonk \
-c "SELECT username, type, twofa_type FROM users WHERE twofa_type <> 'none';"
結果が空ならTOTPを使うアカウントはなく、どちらの不具合もパスワード以上のものを攻撃者に与えません。
修正方法
7.0.0-beta.2へ更新してください。両方の修正を含む唯一のリリースです。
- 開始前にデータベースをバックアップします。7.0では設定と管理画面が変わり、プロジェクトはアップグレード手順でバックアップを求めています。
docker exec -i <postgres-container> pg_dump -U listmonk listmonk > listmonk-backup.sql - Dockerの場合は、composeファイルのイメージタグを
listmonk/listmonk:latestからlistmonk/listmonk:betaに変更し、アップグレードを実行します。docker compose down docker compose pull docker compose run --rm app ./listmonk --upgrade docker compose up -d app db - バイナリの場合は、listmonkを停止し、バイナリをリリースページの7.0.0-beta.2に置き換え、データベースのアップグレードを実行してからlistmonkを再起動します。
./listmonk --upgrade ./listmonk --version - ソースからのインストールでは、
v7.0.0-beta.2タグに切り替え、プロジェクトのMakefileでバイナリをビルドします。ビルド後に手順3と同じく--upgradeを実行します。 - 更新後に、2FAの入力を含めて管理者ログインをテストします。その後、実行中のバージョンが7.0.0-beta.2と表示されることを確認します。
7.0は互換性のない変更を含むベータです。管理画面は書き直され、設定APIはapp.from_emailの形式を変え、SESとAzureのWebhookは要件が変わります。本番環境を更新する前にリリースノートを読み、まずステージングで試してください。
ベータへ移行できない場合は、リバースプロキシで2つのログインエンドポイントにレート制限をかけ、/adminを信頼できるネットワークに制限します。POST /admin/loginとPOST /admin/login/twofaの制限は総当たりを遅くしますが、リセットによる回避は塞ぎません。その経路はコードを一度も要求しないためです。
根本原因は何ですか?
listmonkは短命の認証トークンをinternal/tmptokensパッケージのメモリ内マップに保持します。cmd/auth.goの管理者ログイン処理は、パスワードリセットのトークンと2FAのトークンの両方にこのマップを使います。2つの不具合は、これらのトークンの数え方と消費の仕方に由来します。
リセットの不具合はdoResetPasswordにあります。パスワード変更が成功すると、この関数はユーザーのセッションを削除し、そのまま新しいセッションを作成します。
// Invalidate all existing sessions for the user after password reset.
if err := a.core.DeleteUserSessions(user.ID, ""); err != nil {
a.log.Printf("error destroying sessions after password reset for user_id=%d: %v", user.ID, err)
}
// Log the user in directly without forcing a manual login right after password change.
if err := a.auth.SaveSession(user, "", c); err != nil {
return err
}
// Redirect to the admin page.
return c.Redirect(http.StatusFound, uriAdmin)
この関数はuser.TwofaTypeを読みません。パスワードをリセットしたユーザーは、アカウントでTOTPが有効でも即座にサインインします。したがって、リセットを完了できる攻撃者はコードなしで完全なセッションを得ます。
総当たりの不具合はトークンのカウンターにあります。TwofaPageは/admin/login/twofaへの各リクエストでtmptokens.Check(token)を呼びます。Checkはトークンごとのカウンターを増やし、回数が15を超えるとトークンを削除します。
// Increment the rate limit counter.
token.Count++
// Check if max attempts exceeded.
if token.Count > maxTries {
delete(tokens, id)
return nil, Err
}
この制限はレート制限に見えますが、ユーザーではなくトークンに結び付いています。doLoginはパスワードログインのたびに新しいトークンを作ります。
// Set the token.
tmptokens.Set(token, twofaTokenTTL, user.ID)
SetはCountがゼロの新しいTokenを作り、ログインのエンドポイント自体には試行回数の上限がありません。パスワードを持つ攻撃者は、15回の推測のたびに新しいトークンを要求できるため、カウンターは何度でもゼロに戻ります。
どのように悪用されますか?
リセットの経路は完了したパスワードリセットを必要とします。総当たりの経路はアカウントのパスワードを必要とします。どちらもTOTPで保護されたアカウントのセッションで終わります。
CVE-2026-128333、リセットによる回避:
- 攻撃者は対象アカウントのパスワードリセットメールをlistmonkに送らせるか、すでに送られたリセットリンクを別の方法で入手します。
- 攻撃者はリンクを開き、メールで届いたトークンとともに新しいパスワードを
/admin/resetへ送信します。 doResetPasswordはトークンを検証し、パスワードを更新し、既存のセッションを削除します。- 同じ関数が
SaveSessionを直接呼ぶため、応答はTOTPの入力を求めずにセッションCookieを設定します。 - 攻撃者はそのアカウントとして認証済みのセッションを持ちます。
CVE-2026-128332、TOTPの総当たり:
- 攻撃者は既知のユーザー名とパスワードを
POST /admin/loginへ送信します。 - listmonkはパスワードを受け入れ、カウンターがゼロの2FAトークンを作り、
/admin/login/twofaへリダイレクトします。 - 攻撃者は
POST /admin/login/twofaへ最大15個のコードを送信します。トークンは15回目の確認後に削除されます。 - 攻撃者は手順1から繰り返します。パスワードログインのたびに新しいトークンと15回の推測が生まれます。
- 正しいコードがセッションを作り、攻撃者は停止します。
公開された実証コードはなく、どちらの経路も不正な形式のリクエストを必要としません。/admin/loginの唯一の速度調整は100ミリ秒の最小応答時間です。
攻撃の流れは?
パスワードを知るかリセットを完了できる攻撃者は、どちらかの経路でTOTPコードなしの認証済み管理者セッションを得ます。
ループが総当たりの不具合です。新しいパスワードログインが試行カウンターをリセットします。直接の辺がリセットの不具合です。リセット後のログインは2FAページを完全に飛ばします。
攻撃者に何ができますか?
管理者セッションに到達した攻撃者は、そのアカウントが管理するlistmonkのインストールを支配します。購読者リストとその個人データ、キャンペーン、テンプレート、設定、他のユーザーアカウントが含まれます。Super Adminロールは、送信メールの設定を含むインストール全体の設定を変更できます。
リセットの経路では、攻撃者はパスワードリセットを完了する必要があり、通常は対象のメールを読むことを意味します。総当たりの経路では、アカウントのパスワードと多数のログインリクエストが必要です。どちらも/adminへのネットワークアクセスを必要とし、TOTPが有効なアカウントでのみ機能します。TOTPがなければ回避できる第二要素はなく、影響は盗まれたパスワードと同じです。
VulnCheckのアドバイザリは、管理者セッションからホスト上のコード実行に至る経路を説明していません。影響はlistmonkのデータと設定の完全な制御であり、サーバーの制御ではないと考えてください。
修正が機能する理由
2つのコミットが2つの経路を塞ぎます。リセットの流れは通常のログインと同じ関数を通り、TOTPのカウンターは単一のトークンではなくユーザーに結び付きます。
リセットの修正は、リセット後のログインをfinishPasswordLoginに通します。
// Log the user in directly without forcing a manual login right after password change.
- if err := a.auth.SaveSession(user, "", c); err != nil {
- return err
- }
-
- // Redirect to the admin page.
- return c.Redirect(http.StatusFound, uriAdmin)
+ return a.finishPasswordLogin(c, user, uriAdmin)
}
finishPasswordLoginはuser.TwofaTypeを読み、TOTPのアカウントではセッションを保存する代わりに2FAトークンを作り/admin/login/twofaへリダイレクトします。パスワードリセットはもうコードを飛ばせません。
総当たりの修正は、ユーザーごとに共有の試行予算を与えます。
func (a *App) doTwofaVerify(c *echo.Context, token string, userID int, next string) error {
+ // Share a budget across all login tokens for this user. Only requests with a
+ // valid password-authenticated token reach here. New logins do not reset it.
+ attemptKey := fmt.Sprintf("twofa-attempts:%d", userID)
+ tmptokens.SetNX(attemptKey, twofaTokenTTL, nil)
+ if _, err := tmptokens.Check(attemptKey); err != nil {
+ return echo.NewHTTPError(http.StatusTooManyRequests, a.i18n.T("globals.messages.invalidValue"))
+ }
+
totpCode := strings.TrimSpace(c.FormValue("totp_code"))
SetNXはキーが存在しないか期限切れのときだけ作り、新しいログインはカウンターをリセットしません。Checkも、使い切ったトークンを削除せず期限まで保持するよう変更され、SetNXが予算のキーを再作成するのを防ぎます。同じコミットは、リセットトークンのGet比較を定時間のMatchに置き換え、値が一致したときだけトークンを消費します。
2つのコミットは2つのアドバイザリを覆います。リセットトークンの変更は同じ流れの強化であり、3つ目の脆弱性ではありません。
侵害されたかどうかを確認するには?
VulnCheckもメンテナーも侵害の指標を公開していません。listmonkはTOTP確認の失敗をすべて記録しないため、失敗した試行は痕跡を残さないことがあります。
listmonkの前段にあるリバースプロキシやWebサーバーのログで、繰り返されるログイン通信を探します。総当たりは、同じアドレスからの多数のPOST /admin/loginとそれに続くPOST /admin/login/twofa、および/admin/login/twofaへの多数のリダイレクトとして現れます。
grep -E 'POST /admin/login(/twofa)?' access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
誰も作業していない時間に作られたセッションがないか、セッションテーブルを確認し、ユーザーのloggedin_atの値と比較します。
docker exec -i <postgres-container> psql -U listmonk -d listmonk \
-c "SELECT created_at, data FROM sessions ORDER BY created_at DESC LIMIT 20;"
管理者に、要求していないパスワードリセットメールが届いていないか尋ねます。完了したリセットはアカウントのパスワードを変え、以前のセッションを破棄します。誰も要求していないリセットは、リセット経路の最も明確な兆候です。
年表
- 2026-06-26: listmonk 6.2.0がリリース。修正前の最後の安定版です。
- 2026-11-01: コミット14b25edcが、リセット後のログインを2FAの入力へ通します。
- 2026-11-09: コミット3f62a55cが、ユーザーごとのTOTP試行予算とリセットトークンの強化を加えます。
- 2026-11-10: listmonk 7.0.0-beta.2が両方の修正とともにリリースされます。
- 2026-11-13: VulnCheckがCVE-2026-128333とCVE-2026-128332を公開します。
よくある質問
すべてのlistmonkインストールが影響を受けますか?
いいえ。影響を受けるのは6.0.0から6.2.0と7.0.0-beta.1だけで、TOTPが有効なアカウントだけが回避できる第二要素を持ちます。2FAのないインストールは通常どおりパスワード窃取にさらされます。
6.x系の修正はありますか?
いいえ。メンテナーは両方の不具合を7.0.0-beta.2で修正し、6.xのリリースには含まれていません。最新の安定版6.2.0は2026年6月26日に公開されました。
ベータを実行する必要がありますか?
修正版を持つには必要です。7.0.0-beta.2は設定とWebhookに互換性のない変更を含むベータなので、データベースをバックアップし、本番更新の前にテストしてください。リバースプロキシのレート制限と/adminのネットワーク制限は露出を減らしますが、リセット経路は塞ぎません。
更新せずに緩和できますか?
一部は可能です。POST /admin/loginとPOST /admin/login/twofaにレート制限をかけ、/adminを信頼できるアドレスに制限します。これで総当たりは遅くなります。リセットによる回避は残ります。脆弱なコードはリセット成功後に実行され、コードを一度も要求しないためです。
更新後に認証情報をローテーションすべきですか?
侵害を否定できない場合は必要です。管理者パスワードをリセットし、SMTPの認証情報などの設定の秘密をローテーションし、すべてのセッションをサインアウトします。この不具合が与えるのはセッションであり、永続的なインプラントではありません。
攻撃者は何回推測できますか?
パスワードログイン1回につき15回で、新しいログインは新しいトークンを作ります。6桁のTOTPコードには1,000,000通りの値があるため、攻撃者は多くのログインを必要としますが、アプリケーションは繰り返しを止めません。
参考資料
- CVE-2026-128332のレコード、2FAエンドポイント経由のTOTP総当たり: https://www.cve.org/CVERecord?id=CVE-2026-128332 (2026年11月13日確認)
- CVE-2026-128333のレコード、パスワードリセット経由の2FA回避: https://www.cve.org/CVERecord?id=CVE-2026-128333 (2026年11月13日確認)
- CVE-2026-128332のVulnCheckアドバイザリ、影響範囲とクレジット: https://www.vulncheck.com/advisories/listmonk-6.0.0-before-7.0.0-beta.2-totp-brute-force-via-admin-login-twofa (2026年11月13日確認)
- CVE-2026-128333のVulnCheckアドバイザリ、影響範囲とクレジット: https://www.vulncheck.com/advisories/listmonk-6.0.0-before-7.0.0-beta.2-2fa-bypass-via-password-reset-flow (2026年11月13日確認)
- CVE-2026-128332のGitHubアドバイザリGHSA-p42g-4rv2-xg2f: https://github.com/advisories/GHSA-p42g-4rv2-xg2f (2026年11月13日確認)
- CVE-2026-128333のGitHubアドバイザリGHSA-h254-fw2f-gggx: https://github.com/advisories/GHSA-h254-fw2f-gggx (2026年11月13日確認)
- 修正コミット14b25edc、リセット後のログインを2FAの入力へ送る: https://github.com/knadh/listmonk/commit/14b25edc31614a1d661e3af72b3d46b887931534 (2026年11月13日確認)
- 修正コミット3f62a55c、共有のTOTP試行予算を加える: https://github.com/knadh/listmonk/commit/3f62a55c13dbc679b29238e890e4b90d6d4996d8 (2026年11月13日確認)
- listmonk 7.0.0-beta.2のリリース、アップグレードコマンドと互換性のない変更: https://github.com/knadh/listmonk/releases/tag/v7.0.0-beta.2 (2026年11月13日確認)
- listmonk 6.2.0のリリース、修正前の最後の安定版: https://github.com/knadh/listmonk/releases/tag/v6.2.0 (2026年11月13日確認)
- 7.0.0-beta.1タグの脆弱な
tmptokens.CheckとSet: https://github.com/knadh/listmonk/blob/v7.0.0-beta.1/internal/tmptokens/tmptokens.go (2026年11月13日確認) - 7.0.0-beta.1タグの脆弱な
doResetPasswordとdoLogin: https://github.com/knadh/listmonk/blob/v7.0.0-beta.1/cmd/auth.go (2026年11月13日確認) - listmonkのDocker Hubページ、ダウンロード数の出典: https://hub.docker.com/r/listmonk/listmonk (2026年11月13日確認)
- listmonkリポジトリ、スター数の出典: https://github.com/knadh/listmonk (2026年11月13日確認)
- CISAのKnown Exploited Vulnerabilitiesカタログ、両方のCVEを確認: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (2026年11月13日確認、カタログバージョン2026.11.10)