Proof-of-Concept für ein TOCTOU-Rennen in der Erstkonfiguration von Uptime Kuma über Socket.IO, das es einem nicht authentifizierten Angreifer ermöglicht, einen versteckten zweiten Administrator anzulegen und die Authentifizierung zu deaktivieren.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · uptime-kuma-setup-toctou
Uptime Kuma 2.5.5 - Louis Lam
Beim ersten Start zählt Socket.IO setup die Benutzer, führt bcrypt mit Kosten 10 aus und fügt dann INSERTs ein. Der Benutzername ist nur UNIQUE. Es gibt keine Transaktion, keinen Mutex und keine Ein-Zeilen-Beschränkung. Zwei bereits verbundene Sockets, die beide count === 0 sehen, hashen beide und fügen beide ein. Der zusätzliche Admin ist unsichtbar: Es gibt keine Benutzerlisten-UI.
Ein Fremder, der ein frisch gestartetes Kuma im selben Poll wie der Betreiber erreicht, kann einen versteckten zweiten Admin platzieren, die Authentifizierung deaktivieren und sich per Auto-Login in die Monitore des Betreibers einloggen.
| ID | noch keine CVE |
| CWE | CWE-362, CWE-367 |
| CVSS | Hoch: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Produkt | Uptime Kuma |
| Betroffen | bis 2.5.5 (c98982a), leere user-Tabelle beim ersten Start |
| Auth | unauthentifiziert |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Ein frisch gestartetes Kuma erreichen, während der Betreiber /setup absendet. Der Standard-Docker-Bind ist 0.0.0.0:3001. Eine zweite user-Zeile mit einem anderen Benutzernamen einfügen. Sich als dieser Benutzer anmelden, disableAuth setzen und per R.findOne("user") Auto-Login in die Monitore, Benachrichtigungen, API-Schlüssel und Einstellungen des Betreibers einsteigen.
Nachdem die erste Zeile committet wurde, wird ein späteres sequenzielles setup abgelehnt. Das ist nicht „Setup bleibt offen". Es ist ein Race während des bcrypt-Fensters. Bereits initialisierte Instanzen mit einem Admin sind nicht von diesem Bug betroffen.
Ein latenter API-Key-Enable/Disable-IDOR (UPDATE api_key SET active=? WHERE id=? ohne user_id) wird ebenfalls aktiv, sobald zwei Benutzer existieren. Folge der zusätzlichen Zeile, nicht des Lab-Orakels.
Ich habe zuerst SECURITY.md gelesen, dann die Skip-Liste: achtzehn veröffentlichte GitHub-Advisories, plus öffentliches Matomo siteId XSS, plus Vendor-Wontfix Cloud-Metadata-SSRF. Dann zweiundzwanzig Jagden auf Tag 2.5.5 (c98982a): Auth/Setup/2FA, Socket-IDOR, HTTP/Monitor-SSRF, SSTI-Überreste, Path/LFI, Real-Browser, Plugin/Tailscale, Status-Page, docker.sock, Prototype Pollution, Push/Prometheus, Datenbank, Monitor-Exec, Analytics-XSS, Einstellungen, WebSocket-CSRF, Frontend-XSS, Jobs, Apprise, Wartung, Master-Delta. Der größte Teil dieser Latte lieferte nichts Unveröffentlichtes. Auth nicht.
Das Setup des ersten Benutzers ist ein öffentliches Socket.IO-Event. Kein Login. setup macht COUNT(user), dann await passwordHash.generate, dann R.store. passwordHash.generate ist bcrypt.hash mit Kosten 10. Das ist ein Nickerchen, kein Lock. needSetup ist im Speicher und wird nur verwendet, um beim Verbinden "setup" zu senden. Der Schreibpfad ignoriert es und zählt erneut.
Das Schema ist nur Benutzername UNIQUE. knex_init_db.js erzwingt keine einzelne Zeile. Derselbe Benutzername kollidiert (SQLITE_CONSTRAINT). Unterschiedliche Benutzernamen landen beide.
Ich habe louislam/uptime-kuma:2.5.5 auf Loopback mit UPTIME_KUMA_DB_TYPE=sqlite gestartet, damit der v2-Datenbank-Assistent übersprungen wird. bcrypt mit Kosten 10 ist ein Race, keine Garantie beim ersten Versuch. run.sh löscht das Volume und versucht es erneut. Exit 2 ist ein Fehlschlag. Als Nächstes eine frische leere user-Tabelle.
Als es traf: sqlite 1|labadmin und 2|UK-SETUP-TOCTOU-WITNESS. Sequenzielles spätes Setup: Uptime Kuma has been initialized. Zusätzlicher Benutzer eingeloggt, setSettings disableAuth=true. Reconnect-Auto-Login ist R.findOne("user"): niedrigste ID, der Betreiber. prepare2FA otpauth-Identität ist labadmin.
In der Nähe: GHSA-23q2-5gf8-gjpp ist disableAuth Re-Enable, das Sockets nicht trennt. Anderer Bug.
Bereits dokumentierte Irrwege: eine Benutzerzeile nach gleichzeitigem Setup (Race-Fehlschlag, kein Patch); UK-SETUP-TOCTOU-WITNESS fehlt, weil beide Sockets denselben Namen verwendeten; Dashboard-HTML 200 als SUCCESS behandeln; eine Reverse Shell. Theater. Das Orakel sind zwei sqlite-Zeilen, dann disableAuth Auto-Login als ID 1.
cd lab
./run.sh
Ziel nur http://127.0.0.1:18141. Offizielles Image louislam/uptime-kuma:2.5.5, sqlite, erster Start. Den Port nicht außerhalb von Loopback veröffentlichen.
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 serialisieren: eine Transaktion plus eine Ein-Zeilen-Beschränkung oder ein Mutex um COUNT+INSERT. disableAuth Auto-Login nicht als findOne der niedrigsten ID behandeln. Bis dahin das Setup beim ersten Start nicht in einem gemeinsam genutzten Netzwerk exponieren.
server.js setup · password-hash.js · knex_init_db.jsGNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.