Uptime Kuma के first-run Socket.IO सेटअप में TOCTOU रेस के लिए proof-of-concept, जो एक unauthenticated attacker को एक छिपा हुआ दूसरा admin बनाने और auth को disable करने देता है।
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 करता है। Username केवल UNIQUE है। कोई transaction, mutex, या one-row constraint नहीं है। दो पहले से जुड़े sockets जो दोनों count === 0 देखते हैं, दोनों hash करते हैं, दोनों insert करते हैं। अतिरिक्त admin अदृश्य है: कोई user-list UI नहीं है।
एक अजनबी जो ऑपरेटर के साथ उसी poll में ताज़ा शुरू किए गए Kuma को हिट करता है, एक छिपा हुआ दूसरा admin लगा सकता है, auth बंद कर सकता है, और ऑपरेटर के monitors पर auto-login कर सकता है।
| 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 |
| Product | Uptime Kuma |
| Affected | 2.5.5 (c98982a) तक, प्रथम-रन खाली user table |
| Auth | unauthenticated |
| License | GNU Affero GPL v3.0 |
| Lab | केवल 127.0.0.1 |
ऑपरेटर द्वारा /setup सबमिट करने के उसी समय ताज़ा शुरू किए गए Kuma को हिट करें। डिफ़ॉल्ट Docker bind 0.0.0.0:3001 है। एक अलग username के साथ दूसरी user row insert करें। उस user के रूप में लॉग इन करें, disableAuth सेट करें, और R.findOne("user") auto-login के माध्यम से ऑपरेटर के monitors, notifications, API keys, और settings पर पहुँचें।
पहली row commit होने के बाद, बाद का sequential setup अस्वीकार कर दिया जाता है। यह "setup खुला रहता है" नहीं है। यह bcrypt विंडो के दौरान एक race है। admin वाले पहले से initialized instances इस bug से प्रभावित नहीं हैं।
एक latent API-key enable/disable IDOR (UPDATE api_key SET active=? WHERE id=? बिना user_id के) भी दो users होने पर live हो जाता है। यह अतिरिक्त row का follow-on है, lab oracle का नहीं।
मैंने पहले SECURITY.md पढ़ा, फिर skip list: अठारह प्रकाशित GitHub advisories, साथ ही public Matomo siteId XSS, साथ ही vendor-wontfix cloud-metadata SSRF। फिर tag 2.5.5 (c98982a) पर बाईस hunts: auth/setup/2FA, socket IDOR, HTTP/monitor SSRF, SSTI leftovers, path/LFI, real-browser, plugin/tailscale, status-page, docker.sock, prototype pollution, push/prometheus, database, monitor exec, analytics XSS, settings, websocket CSRF, frontend XSS, jobs, Apprise, maintenance, master-delta। उस bar का अधिकांश हिस्सा कुछ भी अप्रकाशित नहीं लौटाया। Auth ने लौटाया।
First-user setup एक public Socket.IO event है। कोई login नहीं। setup COUNT(user) करता है, फिर await passwordHash.generate, फिर R.store। passwordHash.generate bcrypt.hash cost 10 है। यह एक झपकी है, lock नहीं। needSetup in-memory है और केवल connect पर "setup" emit करने के लिए उपयोग किया जाता है। write path इसे ignore करता है और फिर से count करता है।
Schema केवल username UNIQUE है। knex_init_db.js एक row को enforce नहीं करता। समान username collide करता है (SQLITE_CONSTRAINT)। अलग usernames दोनों land करते हैं।
मैंने loopback पर louislam/uptime-kuma:2.5.5 को UPTIME_KUMA_DB_TYPE=sqlite के साथ खड़ा किया ताकि v2 database wizard skip हो जाए। bcrypt cost 10 एक race है, पहली कोशिश में guarantee नहीं। run.sh volume को wipe करता है और retry करता है। Exit 2 एक miss है। अगली बार खाली user table।
जब यह hit हुआ: sqlite 1|labadmin और 2|UK-SETUP-TOCTOU-WITNESS। Sequential late setup: Uptime Kuma has been initialized। अतिरिक्त user लॉग इन हुआ, setSettings disableAuth=true। Reconnect auto-login R.findOne("user") है: lowest id, ऑपरेटर। prepare2FA otpauth identity labadmin है।
पास का GHSA-23q2-5gf8-gjpp disableAuth re-enable है जो sockets को drop करने में विफल रहता है। अलग bug।
पहले से दर्ज गलत मोड़: concurrent setup के बाद एक user row (race miss, patch नहीं); UK-SETUP-TOCTOU-WITNESS गायब क्योंकि दोनों sockets ने समान नाम उपयोग किया; dashboard HTML 200 को SUCCESS मानना; एक reverse shell। नाटक। Oracle दो sqlite rows है, फिर id 1 के रूप में disableAuth auto-login।
cd lab
./run.sh
लक्ष्य केवल http://127.0.0.1:18141। आधिकारिक image louislam/uptime-kuma:2.5.5, sqlite, first-run। port को loopback से बाहर publish न करें।
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 को serialize करें: एक transaction plus one-row constraint, या COUNT+INSERT के चारों ओर एक mutex। disableAuth auto-login को lowest id का findOne न मानें। तब तक, साझा नेटवर्क पर first-run setup को उजागर न करें।
server.js setup · password-hash.js · knex_init_db.jsGNU Affero GPL v3.0। देखें LICENSE। केवल Loopback lab। कोई warranty नहीं।