abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · uptime-kuma-setup-toctou
Uptime Kuma 2.5.5 - Louis Lam
首次运行时,Socket.IO 的 setup 会先 COUNT 用户,再以 cost 10 进行 bcrypt 哈希,然后 INSERT。用户名仅有 UNIQUE 约束。没有事务、互斥锁或单行约束。两个已经连接的 socket 如果都看到 count === 0,就会各自哈希、各自插入。多出来的管理员是不可见的:没有用户列表 UI。
一个陌生人在操作员轮询的同一时刻访问刚启动的 Kuma,就能植入一个隐藏的第二个管理员,关闭认证,并借助自动登录接管操作员的监控项。
| ID | 暂无 CVE |
| CWE | CWE-362, CWE-367 |
| CVSS | High: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| 产品 | Uptime Kuma |
| 受影响版本 | 直至 2.5.5(c98982a),首次运行且 user 表为空 |
| 认证 | 无需认证 |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1 |
在操作员提交 /setup 的同一时刻访问刚启动的 Kuma。默认 Docker 绑定为 0.0.0.0:3001。插入一行使用不同用户名的第二条 user 记录。以该用户登录,设置 disableAuth,并借助 R.findOne("user") 自动登录接管操作员的监控项、通知、API 密钥和设置。
第一行提交后,后续的顺序 setup 会被拒绝。这并不是“setup 一直开放”。这是 bcrypt 窗口期内的竞态。已经初始化并存在管理员的实例不受此漏洞影响。
一个潜在的 API 密钥启用/禁用 IDOR(UPDATE api_key SET active=? WHERE id=?,没有 user_id)在存在两个用户后也会变为可利用。这是多余行的后续影响,而非实验环境中的判定依据。
我先阅读了 SECURITY.md,然后是跳过列表:十八个已发布的 GitHub 安全公告,加上公开的 Matomo siteId XSS,加上厂商不予修复的云元数据 SSRF。接着在标签 2.5.5(c98982a)上进行了二十二轮排查:认证/setup/2FA、socket IDOR、HTTP/监控 SSRF、SSTI 残留、路径/LFI、真实浏览器、插件/tailscale、状态页、docker.sock、原型污染、push/prometheus、数据库、监控执行、分析 XSS、设置、websocket CSRF、前端 XSS、任务、Apprise、维护、master-delta。其中大部分排查都没有返回未公开的问题。认证部分则不然。
首个用户 setup 是一个公开的 Socket.IO 事件。无需登录。setup 会执行 COUNT(user),然后 await passwordHash.generate,然后 R.store。passwordHash.generate 是 bcrypt.hash,cost 为 10。那只是打个盹,不是锁。needSetup 是内存中的,仅用于在连接时发出 "setup"。写入路径忽略它并重新计数。
schema 中用户名仅有 UNIQUE 约束。knex_init_db.js 不强制单行。相同用户名会冲突(SQLITE_CONSTRAINT)。不同用户名则都会写入。
我在回环地址上启动了 louislam/uptime-kuma:2.5.5,并设置 UPTIME_KUMA_DB_TYPE=sqlite,从而跳过 v2 数据库向导。bcrypt cost 10 是竞态,第一次尝试并不保证成功。run.sh 会清空卷并重试。退出码 2 表示未命中。下一次是全新的空 user 表。
命中时:sqlite 中为 1|labadmin 和 2|UK-SETUP-TOCTOU-WITNESS。顺序的延迟 setup:Uptime Kuma has been initialized。多余用户登录成功,setSettings disableAuth=true。重连自动登录是 R.findOne("user"):最低 id,即操作员。prepare2FA 的 otpauth 身份是 labadmin。
附近的 GHSA-23q2-5gf8-gjpp 是 disableAuth 重新启用时未能断开 socket。不同的漏洞。
已记录的弯路:并发 setup 后只有一行用户(竞态未命中,而非补丁);UK-SETUP-TOCTOU-WITNESS 缺失是因为两个 socket 使用了相同名称;将仪表盘 HTML 200 视为 SUCCESS;一个反向 shell。都是做戏。判定依据是两行 sqlite 记录,然后以 id 1 身份 disableAuth 自动登录。
cd lab
./run.sh
目标仅为 http://127.0.0.1:18141。官方镜像 louislam/uptime-kuma:2.5.5,sqlite,首次运行。不要将端口发布到回环之外。
user-rows [('1', 'labadmin'), ('2', 'UK-SETUP-TOCTOU-WITNESS')]
race-count=2 witness=True
sequential-late-setup initialized=True
setSettings-disableAuth ack ok
reconnect autoLogin=True loginRequired=False
bonus-autologin-identity operator=True id1=labadmin auto_is_id1=True
SUCCESS uptime-kuma-setup-toctou
将 setup 串行化:使用事务加单行约束,或在 COUNT+INSERT 周围加互斥锁。不要将 disableAuth 自动登录视为对最低 id 的 findOne。在此之前,不要在共享网络上暴露首次运行 setup。
server.js setup · password-hash.js · knex_init_db.jsGNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。无任何担保。