إثبات مفهوم لحالة تسابق TOCTOU في إعداد Socket.IO الخاص بالتشغيل الأول لـ Uptime Kuma، مما يتيح لمهاجم غير مُصادَق عليه زرع مسؤول ثانٍ مخفي وتعطيل المصادقة.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · uptime-kuma-setup-toctou
Uptime Kuma 2.5.5 - Louis Lam
في أول تشغيل، يقوم Socket.IO setup بعدّ المستخدمين، ثم bcrypt بتكلفة 10، ثم INSERT. اسم المستخدم UNIQUE فقط. لا توجد معاملة (transaction) ولا mutex ولا قيد صف واحد. مقبسان متصلان بالفعل يريان كلاهما count === 0 يقومان بالتجزئة، وكلاهما يُدخل. المدير الإضافي غير مرئي: لا توجد واجهة قائمة مستخدمين.
غريب يضرب 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 |
| Product | Uptime Kuma |
| Affected | حتى 2.5.5 (c98982a)، جدول user فارغ في أول تشغيل |
| Auth | بدون مصادقة |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 فقط |
اضرب Kuma حديث التشغيل في نفس الوقت الذي يرسل فيه المشغّل /setup. الربط الافتراضي في Docker هو 0.0.0.0:3001. أدخل صف user ثانٍ باسم مستخدم مختلف. سجّل الدخول بذلك المستخدم، واضبط disableAuth، وانطلق عبر تسجيل الدخول التلقائي R.findOne("user") إلى مراقبات المشغّل وإشعاراته ومفاتيح API وإعداداته.
بعد تثبيت الصف الأول، يُرفض setup التسلسلي اللاحق. هذا ليس "يبقى الإعداد مفتوحًا". إنه سباق خلال نافذة bcrypt. النسخ المهيّأة مسبقًا مع مدير ليست هذا الخلل.
كما يصبح IDOR كامن لمفتاح API لتفعيل/تعطيل (UPDATE api_key SET active=? WHERE id=? بدون user_id) نشطًا بمجرد وجود مستخدمين. تابع للصف الإضافي، وليس لأداة التحقق في المختبر.
قرأت SECURITY.md أولاً، ثم قائمة التخطي: ثمانية عشر تنبيهًا منشورًا على GitHub، بالإضافة إلى XSS العام في Matomo siteId، بالإضافة إلى SSRF لبيانات السحابة الوصفية الذي رفضته الجهة المصنّعة. ثم اثنتان وعشرون عملية بحث على الوسم 2.5.5 (c98982a): المصادقة/الإعداد/2FA، IDOR في socket، SSRF في HTTP/المراقبة، بقايا SSTI، path/LFI، متصفح حقيقي، plugin/tailscale، صفحة الحالة، docker.sock، تلوث النموذج الأولي، push/prometheus، قاعدة البيانات، تنفيذ المراقبة، XSS في التحليلات، الإعدادات، CSRF في websocket، XSS في الواجهة الأمامية، المهام، Apprise، الصيانة، master-delta. معظم ذلك العائق لم يُرجع شيئًا غير منشور. المصادقة لم تفعل.
إعداد المستخدم الأول هو حدث Socket.IO عام. بدون تسجيل دخول. setup يقوم بـ COUNT(user)، ثم await passwordHash.generate، ثم R.store. passwordHash.generate هو bcrypt.hash بتكلفة 10. تلك غفوة، وليست قفلاً. needSetup في الذاكرة ويُستخدم فقط لإرسال "setup" عند الاتصال. مسار الكتابة يتجاهله ويعيد العدّ.
المخطط هو username UNIQUE فقط. knex_init_db.js لا يفرض صفًا واحدًا. نفس اسم المستخدم يتعارض (SQLITE_CONSTRAINT). أسماء المستخدمين المختلفة تنجح كلتاهما.
أقمت louislam/uptime-kuma:2.5.5 على loopback مع UPTIME_KUMA_DB_TYPE=sqlite بحيث يتم تخطي معالج قاعدة بيانات v2. bcrypt بتكلفة 10 هو سباق، وليس ضمانًا من المحاولة الأولى. run.sh يمسح الحجم ويعيد المحاولة. الخروج 2 يعني فشل. جدول user فارغ جديد في المرة التالية.
عندما نجح: sqlite 1|labadmin و 2|UK-SETUP-TOCTOU-WITNESS. الإعداد التسلسلي المتأخر: Uptime Kuma has been initialized. المستخدم الإضافي سجّل الدخول، setSettings disableAuth=true. إعادة الاتصال بتسجيل الدخول التلقائي هي R.findOne("user"): أدنى id، المشغّل. هوية otpauth في prepare2FA هي labadmin.
GHSA-23q2-5gf8-gjpp القريب هو فشل إعادة تفعيل disableAuth في إسقاط المقابس. خلل مختلف.
المنعطفات الخاطئة المسجّلة مسبقًا: صف مستخدم واحد بعد الإعداد المتزامن (فشل السباق، وليس تصحيحًا)؛ UK-SETUP-TOCTOU-WITNESS مفقود لأن كلا المقبسين استخدما نفس الاسم؛ اعتبار HTML لوحة التحكم 200 كـ SUCCESS؛ reverse shell. مسرح. أداة التحقق هي صفان في sqlite، ثم تسجيل الدخول التلقائي disableAuth كـ id 1.
cd lab
./run.sh
الهدف فقط http://127.0.0.1:18141. الصورة الرسمية louislam/uptime-kuma:2.5.5، sqlite، أول تشغيل. لا تنشر المنفذ خارج loopback.
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: معاملة (transaction) بالإضافة إلى قيد صف واحد، أو mutex حول COUNT+INSERT. لا تتعامل مع تسجيل الدخول التلقائي disableAuth كـ findOne لأدنى id. حتى ذلك الحين، لا تعرّض إعداد أول تشغيل على شبكة مشتركة.
server.js setup · password-hash.js · knex_init_db.jsGNU Affero GPL v3.0. انظر LICENSE. مختبر loopback فقط. بدون ضمان.