
CakePHP 5.4.2 - عالي - عن بُعد - SQLi - إزاحات النطاق/الإطار في WindowExpression مدمجة في SQL
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cakephp-window-sqli
الفئة: SQLi الوصول: عن بُعد
CakePHP 5.4.2 (887b641) - Cake Software Foundation
CVE-2026-79752 أغلق FunctionsBuilder cast / extract / dateAdd باستخدام ensureSimpleString(). لم يكن WindowExpression::range() و frame() ضمن ذلك الإصلاح. rows() و groups() تأخذان ?int. لا تزال range() تأخذ سلسلة نصية. buildOffsetSql تدمج الإزاحة ونوع الإطار و PRECEDING / FOLLOWING في OVER. لا يُستخدم الـ binder إلا إذا كانت الإزاحة بالفعل ExpressionInterface.
التطبيق الذي يمرر بيانات الطلب إلى func()->count('*')->over()->range($start, $end) يضع سلسلة النص الخاصة بالمُستدعي داخل RANGE BETWEEN بدون ربط وبدون اقتباس.
| المعرّف | لا يوجد CVE بعد |
| الفئة | SQLi |
| الوصول | عن بُعد (تطبيق يمرر بيانات الطلب إلى range() / frame()) |
| CWE | CWE-89 |
| CVSS | مرتفع: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| المنتج | CakePHP (cakephp/database) |
| المتأثر | 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63) |
| المصادقة | تطبيق يمرر بيانات الاستعلام/النموذج إلى ->over()->range($start, $end) / frame() |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
المستخدم القادر على تعيين حد النافذة الذي يمرره التطبيق (تحليلات "آخر N أيام"، منشئ تقارير) يغيّر جملة OVER إلى SQL إضافي.
| الصلاحية | الطريقة / نقطة الوصول | النوع | مُثبت مختبريًا أم مُستنتج |
|---|---|---|---|
حقن جزء SQL في OVER (RANGE BETWEEN ...) | GET /?mode=range&start= إلى AggregateExpression::range($start, $end) / WindowExpression::frame() | سلامة SQL المُجمّع | مُثبت مختبريًا. SQL المُجمّع هو RANGE BETWEEN 1) /* CAKEPHP-WINDOW-SQLI-WITNESS */ -- PRECEDING مع binder فارغ |
| قراءة أو تعديل الصفوف بصلاحية مستخدم قاعدة بيانات التطبيق | نفس الاستعلام، مُنفّذ | سلامة / استطلاع قاعدة البيانات | مُستنتج من الحقن. هذا المختبر للتجميع فقط |
| تغيير نوع الإطار أو الاتجاه بالطريقة نفسها | frame($type, $offset, $direction, ...) تدمج تلك السلاسل أيضًا | سلامة SQL المُجمّع | مُستنتج من sql() / buildOffsetSql(). استخدم المختبر range() |
كسر rows() / groups() بالطريقة نفسها | مُحدّدة النوع ?int | مُغلقة | مُثبت مختبريًا أنها مُغلقة. rows(3) هي ROWS BETWEEN 3 PRECEDING، غير مُقتبسة، غير مربوطة |
مُغلقة على هذا الإصدار: FunctionsBuilder cast / extract / dateAdd / jsonValue (CVE-2026-79752 / CVE-2026-77635)؛ Query::limit / offset (int مُحدّد النوع). لا يستدعي cakephp/app الافتراضي range() ببيانات سلسلة استعلام. المتبقي هو واجهة النافذة الموثّقة. ليس RCE بدون مصادقة على تطبيق هيكلي. ليس shell.
متبقيات نفس الإصدار: collate SQLi، attachment CRLF، cache unserialize.
ثبّتُ cakephp/cakephp 5.4.2 بعد GHSAs الخاصة بـ FunctionsBuilder SQLi لعام 2026. عشر عمليات بحث على ذلك الإصدار. CSRF / FormProtection، View LFI، BodyParser XXE، mass assignment، helper XSS، و clientIp() بدون trustProxy جاءت فارغة في هذا المعيار. لا يزال WindowExpressionTest::testRange يؤكد:
$w = (new WindowExpression())->range("'1 day'", "'10 days'");
buildOffsetSql لا تربط أبدًا إزاحة نصية:
protected function buildOffsetSql(
ValueBinder $binder,
ExpressionInterface|string|int|null $offset,
string $direction,
): string {
if ($offset === 0) {
return 'CURRENT ROW';
}
if ($offset instanceof ExpressionInterface) {
$offset = $offset->sql($binder);
}
return sprintf('%s %s', $offset ?? 'UNBOUNDED', $direction);
}
$binder غير مستخدم إلا إذا كانت الإزاحة بالفعل ExpressionInterface.
المنعطفات الخاطئة المسجّلة مسبقًا: التعامل مع rows() كالثغرة (إنها int مُحدّد النوع)؛ الادعاء بأن cakephp/app الجاهز يكشف هذا على نموذج عام؛ تنفيذ SQL المحقون في SQLite كأداة استدلال (POC-READY للتجميع فقط)؛ reverse shell. مسرحية.
python3 cakephp-window-sqli-Abraxas-Labs.py
أو
cd lab
./run.sh
استهدف فقط http://127.0.0.1:19440. تطبيق PHP صغير، cakephp/database 5.4.2، SQLite :memory:. يُمرَّر GET /?mode=range&start= كسلسلة نصية إلى range(). GET /?mode=rows&n=3 هو عنصر التحكم الصحيح.
SUCCESS cakephp-window-sqli pin=5.4.2 range-raw=yes rows-numeric=yes bound=no quoted=no CAKEPHP-WINDOW-SQLI-WITNESS
IOC لـ SQL المُجمّع:
SELECT (COUNT(*) OVER (RANGE BETWEEN 1) /* CAKEPHP-WINDOW-SQLI-WITNESS */ -- PRECEDING AND CURRENT ROW)) AS n FROM sqlite_master
عنصر التحكم:
SELECT (COUNT(*) OVER (ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)) AS n FROM sqlite_master
اربط الإزاحات الرقمية. اسمح بنوع الإطار (RANGE / ROWS / GROUPS) والاتجاه (PRECEDING / FOLLOWING) عبر قائمة مسموح بها. بالنسبة لسلاسل الفواصل الزمنية، اقتبسها أو اشترط ExpressionInterface. أضف اختبارًا يتحقق من أن إزاحة range() غير الصحيحة تصبح placeholder أو قيمة نصية مُقتبسة، وليس SQL خام.
887b641de9f7aba06071f98f17edcbc573cbcf63)WindowExpression.php · AggregateExpression.phpGNU Affero GPL v3.0. انظر LICENSE. مختبر loopback فقط. لا ضمان.