Listmonk 2FA 绕过,仅在 7.0 测试版修复(CVE-2026-128332)
listmonk 6.0.0 至 6.2.0 允许持有密码的攻击者绕过 TOTP 双因素认证;唯一修复版本是 7.0.0-beta.2。
TL;DR
- listmonk 的两个缺陷使 TOTP 双因素认证失效:一个在密码重置后跳过验证码,另一个允许攻击者无限次猜测验证码。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 严重、CVSS v3.1 7.4 高危。目前没有已知利用,截至 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。
- 至少一个账户,尤其是管理员账户,启用了 TOTP 双因素认证。
- 对于 CVE-2026-128332,攻击者知道该账户的密码。对于 CVE-2026-128333,攻击者能为该账户完成密码重置,例如读取其电子邮件。
- 攻击者能够访问
/admin界面。
listmonk 5.x 及更早版本不在公告的影响范围内。没有 TOTP 的账户没有可绕过的第二因素,因此重置缺陷只剩下密码这一道障碍。暴力破解缺陷需要猜测六位验证码,并需要密码作为起点。
两个分支中只有一个有修复版本。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 的要求也发生了变化。升级生产环境前请阅读发布说明,并先在预发布环境测试。
如果无法迁移到测试版,请在反向代理上对两个登录端点限速,并将 /admin 限制在可信网络。对 POST /admin/login 和 POST /admin/login/twofa 的限速会减慢暴力破解。它不能关闭重置绕过,因为该路径从不要求验证码。
根本原因是什么?
listmonk 将短期认证令牌保存在 internal/tmptokens 包的内存映射中。cmd/auth.go 中的管理员登录流程将这个映射同时用于密码重置令牌和 2FA 令牌。两个缺陷都来自这些令牌的计数和消费方式。
重置缺陷位于 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 个验证码。第十五次检查后令牌被删除。 - 攻击者从第 1 步重复。每次密码登录都会创建新令牌和另外 15 次猜测。
- 正确的验证码创建会话,攻击者停止。
没有公开的漏洞利用代码,两条路径都不需要畸形请求。/admin/login 上唯一的节奏控制是 100 毫秒的最低响应时间。
攻击如何展开?
知道密码或能完成重置的攻击者,通过任一路径获得无需 TOTP 验证码的已认证管理员会话。
循环是暴力破解缺陷:新的密码登录重置尝试计数器。直接边是重置缺陷:重置后的登录完全跳过 2FA 页面。
攻击者能做什么?
到达管理员会话的攻击者控制该账户管理的 listmonk 安装。这包括订阅者列表及其个人数据、活动、模板、设置和其他用户账户。Super Admin 角色可以更改整个安装的设置,包括外发邮件配置。
重置路径要求攻击者完成密码重置,这通常意味着读取目标的电子邮件。暴力破解路径要求账户密码和大量登录请求。两者都需要对 /admin 的网络访问,且仅对启用了 TOTP 的账户有效。没有 TOTP 就没有可绕过的第二因素,影响与密码被盗相同。
VulnCheck 的公告没有描述从管理员会话到主机代码执行的路径。请将影响视为对 listmonk 数据和配置的完全控制,而不是对服务器的控制。
为什么修复有效?
两个提交关闭了两条路径。重置流程现在经过与普通登录相同的函数,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,仅当值匹配时才消费令牌。
两个提交覆盖了两个公告。重置令牌的变更是同一流程的加固,不是第三个漏洞。
如何检查是否已遭入侵?
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 凭据等设置密钥,并注销所有会话。这些缺陷给攻击者的是会话,而不是持久化植入。
攻击者能猜多少次?
每次密码登录 15 次,而每次新登录都会创建新令牌。六位 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 已知被利用漏洞目录,已核查两个 CVE:https://www.cisa.gov/known-exploited-vulnerabilities-catalog(2026 年 11 月 13 日核查,目录版本 2026.11.10)